├── .gitignore ├── .gitmodules ├── .idea └── vcs.xml ├── .travis.yml ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── build.sh ├── doc ├── bft.jpg ├── dpos.png ├── flow.gliffy └── rfc.md ├── examples ├── .bash_history ├── bft.rs ├── c1.toml ├── c2.toml ├── c3.toml ├── c4.toml ├── c5.toml ├── c6.toml ├── config.toml ├── docker_build.sh ├── docker_start.sh └── p2p.rs ├── rustfmt.toml └── src ├── api └── mod.rs ├── cmd └── mod.rs ├── common └── mod.rs ├── config └── mod.rs ├── consensus ├── backend.rs ├── config.rs ├── consensus.rs ├── dpos │ ├── delegates.rs │ ├── mod.rs │ ├── slot.rs │ └── util.rs ├── engine.rs ├── error.rs ├── events.rs ├── mod.rs ├── pbft │ ├── core │ │ ├── back_log.rs │ │ ├── commit.rs │ │ ├── core.rs │ │ ├── mod.rs │ │ ├── new_header.rs │ │ ├── prepare.rs │ │ ├── preprepare.rs │ │ ├── request.rs │ │ ├── round_change.rs │ │ ├── round_change_set.rs │ │ ├── round_state.rs │ │ ├── timer.rs │ │ └── types.rs │ └── mod.rs ├── types.rs └── validator.rs ├── core ├── actor.rs ├── chain.rs ├── genesis.rs ├── ledger.rs ├── mod.rs ├── transaction_pool.rs └── tx_pool.rs ├── error └── mod.rs ├── lib.rs ├── logger └── mod.rs ├── minner └── mod.rs ├── mocks ├── mock_config.toml ├── mod.rs └── utils.rs ├── p2p ├── codec.rs ├── config.rs ├── discover_service.rs ├── mod.rs ├── node.rs ├── p2p │ ├── Cargo.toml │ ├── doc │ │ ├── Sequence Diagram0.asta │ │ ├── Sequence Diagram0.asta.bak │ │ └── roadmap │ └── src │ │ ├── client.rs │ │ ├── codec.rs │ │ ├── codec2.rs │ │ ├── kad │ │ ├── base.rs │ │ ├── knodetable.rs │ │ ├── mod.rs │ │ ├── protocol.rs │ │ ├── service.rs │ │ └── utils.rs │ │ ├── lib.rs │ │ ├── peer.rs │ │ ├── server.rs │ │ └── session.rs ├── protocol.rs ├── server.rs └── session.rs ├── pprof └── mod.rs ├── protocol └── mod.rs ├── store ├── base_index.rs ├── entry.rs ├── iter.rs ├── list_index.rs ├── map_index.rs ├── mod.rs ├── schema.rs └── types.rs ├── subscriber ├── events.rs └── mod.rs ├── types ├── block.rs ├── mod.rs ├── transaction.rs └── votes.rs └── util └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | .idea/* 12 | bft/target/* 13 | parity-common/* 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "parity-common"] 2 | path = parity-common 3 | url = https://github.com/laohanlinux/parity-common.git 4 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - nightly-2019-04-07 5 | 6 | script: 7 | - git submodule add --force https://github.com/laohanlinux/parity-common.git 8 | - cargo build --verbose --all 9 | - cargo test --verbose --all 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "consensus" 3 | version = "0.1.0" 4 | authors = ["Rg "] 5 | description = "bft consensus" 6 | license = "GPL-3.0" 7 | edition = "2018" 8 | 9 | [dependencies] 10 | chrono = { version = "0.4", features = ["serde"] } 11 | chrono-humanize = "0.0.11" 12 | serde = "1.0" 13 | serde_derive = "1.0" 14 | serde_json = "1.0" 15 | runtime-fmt = "*" 16 | bigint = "4.4.1" 17 | byteorder = "1" 18 | rand = "0.5" 19 | hex = "*" 20 | sha3 = "0.7.3" 21 | rlp = "0.2.4" 22 | lazy_static = "1.1.0" 23 | crossbeam = {git = "https://github.com/crossbeam-rs/crossbeam.git"} 24 | ethereum-types = "0.5.2" 25 | lru_time_cache = "0.8.0" 26 | eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" } 27 | cryptocurrency-kit = {git = "https://github.com/laohanlinux/cryptocurrency-kit-rs.git", tag = "v0.1.1"} 28 | kvdb-rocksdb = {path = "./parity-common/kvdb-rocksdb"} 29 | kvdb = {path = "./parity-common/kvdb"} 30 | transaction-pool = {path = "./parity-common/transaction-pool"} 31 | log = "0.4" 32 | env_logger = "0.6.0" 33 | priority-queue = "0.5.2" 34 | evmap = "4.0.0" 35 | actix = "0.7" 36 | actix-broker = "0.1.6" 37 | actix-web-async-await = "0.1.0" 38 | failure = "0.1.3" 39 | #futures = "0.1.26" 40 | tokio = "0.1" 41 | tokio-threadpool = "0.1.9" 42 | bytes = "0.4" 43 | clap = "2.32.0" 44 | toml = "0.4" 45 | serde_millis = "0.1.1" 46 | parking_lot = {version = "0.6", features = ["nightly"]} 47 | uuid = { version = "0.7", features = ["v5"] } 48 | flame = "0.2.2" 49 | tokio-signal = "0.2" 50 | tide = "0.0.5" 51 | http = "0.1" 52 | futures-preview = "0.3.0-alpha.13" 53 | 54 | [dependencies.libp2p] 55 | git = "https://github.com/laohanlinux/rust-libp2p.git" 56 | 57 | [profile.release] 58 | debug = true 59 | 60 | [dependencies.tap] 61 | git = "https://git.myrrlyn.net/myrrlyn/tap" 62 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM zhrong/bft 2 | 3 | MAINTAINER 0x80 4 | 5 | COPY examples/c1.toml /data/ 6 | COPY examples/c2.toml /data/ 7 | COPY examples/c3.toml /data/ 8 | COPY examples/c4.toml /data/ 9 | COPY examples/c5.toml /data/ 10 | COPY examples/docker_build.sh /data/ 11 | COPY examples/docker_start.sh /data/ 12 | 13 | WORKDIR /root 14 | 15 | RUN /data/docker_start.sh && tail -f /tmp/c1.log 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # consensus-rs [![Build Status](https://travis-ci.org/laohanlinux/consensus-rs.svg?branch=master)](https://travis-ci.org/laohanlinux/consensus-rs) 2 | Implement multiple blockchain consensus, including raft, pbft, paxos, dpos, power 3 | 4 | - [x] pbft 5 | - [ ] raft 6 | - [ ] paxos 7 | - [ ] dpos 8 | - [ ] power 9 | 10 | ## start example 11 | 12 | ``` sh 13 | # git submodule add --force https://github.com/laohanlinux/parity-common.git 14 | 15 | # ./build.sh 16 | ``` 17 | 18 | ## RUN Docker 19 | 20 | ``` sh 21 | docker build -t tt . 22 | ``` 23 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version="nightly-2019-04-07" 4 | rustup override set $version 5 | 6 | pkill bft 7 | 8 | cargo build --example bft 9 | 10 | function cluster() { 11 | RUST_BACKTRACE=full RUST_LOG=info ./target/debug/examples/bft start --config examples/c1.toml 1> /tmp/c1.log 2>&1 & 12 | RUST_BACKTRACE=full RUST_LOG=info ./target/debug/examples/bft start --config examples/c2.toml 1> /tmp/c2.log 2>&1 & 13 | RUST_BACKTRACE=full RUST_LOG=info ./target/debug/examples/bft start --config examples/c3.toml 1> /tmp/c3.log 2>&1 & 14 | RUST_BACKTRACE=full RUST_LOG=info ./target/debug/examples/bft start --config examples/c4.toml 1> /tmp/c4.log 2>&1 & 15 | RUST_BACKTRACE=full RUST_LOG=info ./target/debug/examples/bft start --config examples/c5.toml 1> /tmp/c5.log 2>&1 & 16 | } 17 | 18 | echo "run in 5 nodes" 19 | pkill bft 20 | cluster 21 | -------------------------------------------------------------------------------- /doc/bft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laohanlinux/consensus-rs/ebf0879ea3cf653c62ac79ff658c804a3bddcc6f/doc/bft.jpg -------------------------------------------------------------------------------- /doc/dpos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laohanlinux/consensus-rs/ebf0879ea3cf653c62ac79ff658c804a3bddcc6f/doc/dpos.png -------------------------------------------------------------------------------- /doc/rfc.md: -------------------------------------------------------------------------------- 1 | # 共识 2 | 3 | - 为什么开发这个项目? 4 | 5 | 主要是个人比较喜欢这方面的东西,且以前在做分布式云存储的时候,也经常遇到这些算法的问题。不过存储用到的算法和`Paxos`这类还是有些区别的,业界用到 6 | 较多的还是亚马逊的`Dynamo`算法(`Quorum`,`Consistent Hash`),`w + r > n` 这种模型; 又或者`MetaServer` + `Master + Slave`这种模型(`Fastdfs`,`Tfs`,元数据可以使用`Paxos`去实现集群来管理),但不管那种都涉及到`CAP`的终极问题。 7 | 8 | 而`Paxos`这类了解程度还是停留在看小说的认识上,虽然在工作中也做了不少的东西,但也仅仅是应用以及白皮书阶段而已,`BFT`就更少了,要不是区块链的兴起,很难想象`拜占庭容错`算法适合的场景,长期躺尸的节奏。 9 | 10 | - 为什么采用Rust语言 11 | 12 | 一直想使用一门底层的语言,`c/c++`长年不用,其他语言嘛,还不如`Go`来得酸爽。毫无疑问,对本来说,`Rust`是现阶段最好的选择-`安全/高性能`,当然难度也是很高的,至少`Rust`目前是本人感到最难入门的一门语言了。可以说,`Rust`是一门值得研究的语言,够研究几十年了。 13 | 14 | ## BFT 15 | 16 | 目前采用的是`PBFT`, 最基本的版本。麻雀虽小,五肺俱全,上个总图先: 17 | 18 | ![](bft.jpg) 19 | 20 | 每个节点使用`Validator`来标示。 21 | 22 | ### validator 状态 23 | validator每一轮次(Height+Round)共有5种状态: 24 | `AccpetRequest`, `PrePrepared`, `Prepared`, `Committed`,`round change`。 25 | 26 | - AccpetRequest 27 | 28 | 启动新轮次,进入等待提案阶段 29 | 30 | - PrePrepared 31 | 32 | 收到预准备的消息(包含提案),即预准备阶段 33 | 34 | - Prepared 35 | 36 | 收到`+2/3`准备的消息,进入准备阶段 37 | 38 | - Committed 39 | 40 | 收到`+2/3`提交的消息,进入准备阶段 41 | 42 | - round change 43 | 44 | 这是一种特殊的状态,即在规定的时间内,本轮次无法达成,节点超时后,进入`Round change`状态,进行投票,切换视图。 45 | 46 | ### 锁机制 47 | 48 | 要做到拜占庭容错,除了防止`坏人`外,还得防止`好人`出错。比如下面的场景: 49 | `A, B, C, D, E` 都到了最后一个阶段,都收到了`3`票`commit`,然后`E`投出一票`commit`,由于网络的原因,`A,B,C,D` 没有收到来之`E`的`commit`消息,`E` 自己`commit`了,而其他节点在本轮次中未达成共识,进而切换视图,继续共识。那么这时候,其他节点该提哪个`提案`?最好是继续上一轮的`提案`,因为`E`在上一轮已经`commit`了,这是时候,如果不提相同的提案,就会出现同一高度(同一序列号)出现了两个合法的提案(区块),在区块链里,就出现了分叉,因为这两个提案都是合法的。而`锁机制`就是为了解决这种情况,那么具体是怎么解决这种问题? 50 | 51 | `Validator` 在接收到`+2/3`的`prepare + commit`时,会锁定在对应的提案,往后的投票validator只会投锁定的提案,不会对其他提案进行投票;如果没有锁定,还是按照正常的流程去投票,如:preprepare时,将军会使用`自己`的准备提案。 52 | 53 | 这样,即使`E`出现上面的情况,其他节点`A, B, C, D`接下来也是使用`E`的`commit` 继续共识,因为他们都锁定在该提案上。这样想想好像挺不错的. 54 | 55 | `But` 哦,`No`,这里又引生出新的问题。 56 | 57 | #### 锁机制带来的问题 58 | 59 | 假设: 60 | 61 | Round: 0, proposal hash `0x22702c...ed16ca` from A 62 | 63 | | A | receive 4 prepare votes | 64 | | :--: | :---------------------: | 65 | | B | receive 3 prepare votes | 66 | | C | receive 1 prepare votes | 67 | | D | receive 3 prepare votes | 68 | | E | No receive | 69 | 70 | 此时除了`A`,其他都是节点都没有收到`+2/3`的票,即`A`锁定在`0x22702c...ed16ca`的提案上。当前`Round`未达成共识,切换视图,继续。 71 | 72 | Round:1,proposal hash `0xc6d378...a95c0e` from B 73 | 74 | | A | No | 75 | | :--: | :---------------------: | 76 | | B | receive 4 prepare votes | 77 | | C | receive 1 prepare votes | 78 | | D | receive 3 prepare votes | 79 | | E | No receive | 80 | 81 | A因为锁定在不同的提案上,所以当前轮,不再投票(只投 `round change`),`B`收到了`4`票,锁定在`0xc6d378...a95c0e`。试想一下,现在网络就好像出现了网络分区的情况,各个区`state`都不同,很难再达成共识。Why? 82 | 83 | - `round 2`: 将军是`C`,无锁,所以提自己的提案:`0x74b9ba...dfb229` 84 | 85 | - `round 3`: 将军是`D`,无锁,所以提自己的提案:`0xd62cf2...0b176e` 86 | 87 | - `round 4`: 将军是`E`,无锁,所以提自己的提案:`0xc0fde9...2b3acc` 88 | 89 | 可以看出一个循环过去了,还是未达成共识。 90 | 91 | `Round `5: 将军是`E`,有锁,所以提锁定的提案:`0x22702c...ed16ca` 92 | 93 | 可能会收到来之`C,D,E`加上自己的那一票,刚好`+2/3`票,即达成共识。 94 | 95 | 这算最好的结果了,试想一下,如果`C, D, E`中的某个节点`Down`机,那么共识将无法达。`A`和`B`只投自己锁定的提案,最多只会收到`2/3`的票,直到`Down`掉的节点再次恢复回来(即使恢复回来,也那么快成共识,因为其他节点的round已经比`Down`机节点的`round`高出好多)。 96 | 97 | 如何解决这些问题? 98 | 99 | 增加`lock hash pprof`,`A`,`B`在发起`PrePrepare`消息时,携带锁证明,证明前`round`大家是有投票给`lock proposal`的,不能够耍赖。其他节点收到证明后就验证(验证签名即可),如果验证通过则也锁定在该提案上。对未锁定的节点来说,可以这样做,但对于已经锁定的节点`B`,它该如果处理? 100 | 101 | 横向证明再加一个指标,除了签名证明外,还要采用优先级策略,即`A`锁定的`round`为`0`,`B`锁定的`round`为`1`,B是不会解锁`A`锁定的提案,`B`在本轮(`round 5`)不会投票给`A`。如果这一轮还未达到共识,`round 6`的时候,`B`广播的提案为`0xc6d378...a95c0e`,其他节点收到`PrePrepare`时,验证`B`的提案,发现`B`锁定提案的轮次(`round 1`)比他们锁定提案的轮次(来之于`A`的锁定高度`round 0`)高,所以他们就把锁重新锁定到`B`锁定的提案`0xc6d378...a95c0e`上,这样就达到了一致性,很快就可以达成共识。 102 | 103 | ### Round change 处理 104 | 105 | `Round change`也可叫做`View Change`,简单来说就是视图切换,切换步骤如下(不同的项目采用的策略不一定相同): 106 | 107 | - 收到比当前小的轮次 108 | 109 | 投赞同票,让其能快速追上最新的步伐 110 | 111 | ```rust 112 | if current_view.round > subject.view.round && subject.view.round > 0 { 113 | // may be peer is less than network node 114 | self.send_round_change(subject.view.round); 115 | return Ok(()); 116 | } 117 | ``` 118 | 119 | - 收到更高的轮次 120 | 121 | `+2/3`票,且处于`WaitForChange`状态,变更轮次 122 | 123 | ```rust 124 | if n >= (current_val_set.two_thirds_majority() + 1) 125 | && (self.wait_round_change && current_view.round < subject.view.round) { 126 | self.send_round_change(subject.view.round); 127 | self.start_new_round(subject.view.round, &vec![]); 128 | return Ok(()); 129 | } 130 | ``` 131 | 132 | > PS: WaitForChange在收到+2/3的票时可以忽略其约束,即+2/3票即可catch up round 133 | 134 | - 超时 135 | 136 | 获取票最多且最大的轮次,投票给它,而不是单循的`Round+1`投票。因为如果收到了某个较高轮次大量的投票,证明大多数人都已经到了相同的高度,各个节点都该往最终一致性的方向投票,加快共识的达成。 137 | 138 | 为了防止恶意节点的攻击,`大多数票`这是一个重要的指标(目前没有限制大多数至少是多少票,如果用于生产环境中,建议不要低于`1/3`的票数)。 139 | 140 | ```rust 141 | // Find the max 142 | let round = self.round_change_set.max_round(); 143 | if round <= current_view.round { 144 | self.send_round_change(current_view.round + 1); 145 | } else { 146 | self.send_round_change(round); 147 | } 148 | ``` 149 | 150 | ### Leader选举 151 | 152 | `Leader = validator[hash(pre_hash + round) % validators.size]` 153 | 154 | ```rust 155 | pub fn fn_selector(blh: &Hash, height: Height, round: u64, vals: &Validators) -> Validator { 156 | assert!(!vals.is_empty()); 157 | let seed = (randon_seed(blh, height, vals) + round) % vals.len() as u64; 158 | vals[seed as usize].clone() 159 | } 160 | 161 | fn randon_seed(blh: &Hash, _height: Height, vals: &Validators) -> u64 { 162 | let blh = blh.as_ref(); 163 | let mut seed_buf = [0; 16]; 164 | for (idx, item) in seed_buf[..8].iter_mut().enumerate() { 165 | *item = blh[idx]; 166 | } 167 | 168 | let block_seed: U128 = U128::from(seed_buf); 169 | (block_seed % U128::from(vals.len())).as_u64() 170 | } 171 | ``` 172 | 173 | 174 | 175 | ### 其他特殊以及改进的情况 176 | 177 | - 提案带宽优化 178 | 179 | 采用`[Tendermint]`的方案,将区块切割成`小份+纠错码`的方式传播 180 | 181 | - 增加`Lock Hash` 182 | - 增加`Validator`管理合约,如剔除/新增某个合约等 183 | - 增加当前区块包括上一个区块的`commit`信息,即当前区块应包含上一区块最后阶段投票的签名 184 | - `Leader`选举更加随机化 185 | - 增加更多的角色,让产生块以及共识的每个阶段又不同的角色去执行,提高公平性以及安全系数 186 | 187 | 资料来源: 188 | 189 | - [Tendermint](https://github.com/tendermint/tendermint) 190 | - [Ont](https://github.com/ontio/ontology) 191 | - [Istanbul Byzantine Fault Tolerance](https://github.com/ethereum/EIPs/issues/650) 192 | 193 | -------------------------------------------------------------------------------- /examples/.bash_history: -------------------------------------------------------------------------------- 1 | ls 2 | cd 3 | ls 4 | exit 5 | ls 6 | cd 7 | ls 8 | exit 9 | cd 10 | ls 11 | ./docker_start.sh 12 | ls 13 | exit 14 | -------------------------------------------------------------------------------- /examples/bft.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate consensus; 3 | extern crate clap; 4 | #[macro_use] 5 | extern crate log; 6 | extern crate actix; 7 | 8 | use ::actix::prelude::*; 9 | use clap::{Arg, App, SubCommand, ArgMatches}; 10 | 11 | use std::sync::mpsc::channel; 12 | 13 | fn main() { 14 | let _config = consensus::config::Config::default(); 15 | let matches = App::new("bft-consensus") 16 | .version("v0.1") 17 | .author("Rg. ") 18 | .about("bft consensus block chain implements") 19 | .subcommand(SubCommand::with_name("start") 20 | .about("star bft-rs") 21 | .arg( 22 | Arg::with_name("config") 23 | .long("config") 24 | .default_value("config.toml") 25 | .short("c") 26 | .value_name("CONFIG"))) 27 | .get_matches(); 28 | let result = run(matches); 29 | if let Err(err) = result { 30 | println!("--->{}", err); 31 | } 32 | } 33 | 34 | fn run(matches: ArgMatches) -> Result<(), String> { 35 | match matches.subcommand() { 36 | ("start", Some(m)) => { 37 | run_start(&m) 38 | } 39 | _ => Err("not matches any command".to_string()) 40 | } 41 | } 42 | 43 | fn run_start(matches: &ArgMatches) -> Result<(), String> { 44 | let config = matches.value_of("config").expect("config is None"); 45 | let (tx, rx) = channel(); 46 | consensus::cmd::start_node(config, tx)?; 47 | rx.recv().unwrap(); 48 | Ok(()) 49 | } -------------------------------------------------------------------------------- /examples/c1.toml: -------------------------------------------------------------------------------- 1 | chain_id = 10 2 | ip = "127.0.0.1" 3 | port = 7691 4 | api_ip = "0.0.0.0" 5 | api_port = 8691 6 | block_period = 1000 # ms 7 | request_time = 5000 # ms 8 | peer_id = "QmbBr2fHwLFKvHkAq1BpbEr4dvR8P6orQxHkVaxeJsJiW8" 9 | ttl = 3000 10 | store = "/tmp/block/c1" 11 | secret = "7f3b0a324e13e5358c3fd686737acd7adf2e5556084ec6d9e48b497082b7ef98" 12 | 13 | [genesis] 14 | validator = ["0x7193d8f91724b39f10cc81e94934c187fa257277", "0x93908f59c6eff007d228398349214acb6b4ac9a4", "0x72d5c75fd6703414aa87f79b3e4797dd09cd9251", "0x58096d35c7a8ff67eba159f33cea7740fc9a737c","0xc759616c865d349ec2afced268fc6f33ff7414a4"] 15 | epoch_time = 2018-09-09T09:09:09.09-09:09 16 | proposer = "0x5701fbd05e77cac003a6894e4b2a3c12287ed313" 17 | gas_used = 10000 18 | extra = "Hello Word!" 19 | [genesis.accounts] 20 | 0x5701fbd05e77cac003a6894e4b2a3c12287ed313 = 500000 21 | 0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d = 500000 22 | 0x3140bda54df92f9453b487afdb3bcce02d154c74 = 500000 23 | 0x7035dafbeac1792ab5b7ed5c903ac63522eb534a = 500000 24 | 0x6730933a2cb6f26af786d7f5979efbdf29049c3a = 500000 25 | 0xfb1bbe89190c9793aec79713e35bdd82a7e5b08b = 500000 26 | 0x1f6f0d11339b5a0db7cef22ae278c15d55178faf = 500000 27 | 0x17f3309f405f53ae3e3c7e98533c58aa0c8c9417 = 500000 28 | 0x6e6e4a7aa7cedac4c4f3e8a7cd363e5f3208e8a6 = 500000 29 | 0x91b73cc738754c4fd7d6a2f0b6b354e293177c80 = 500000 30 | -------------------------------------------------------------------------------- /examples/c2.toml: -------------------------------------------------------------------------------- 1 | chain_id = 10 2 | ip = "127.0.0.1" 3 | port = 7692 4 | api_ip = "0.0.0.0" 5 | api_port = 8692 6 | block_period = 1000 # ms 7 | request_time = 5000 # ms 8 | peer_id = "QmZgaUTxwWRWHbH6sukyFWAaNwPKvZGX9m7epEHGBJHPvt" 9 | ttl = 3000 10 | store = "/tmp/block/c2" 11 | secret = "ec84caf3d58e6bbcdcd6b243203fbaafee19e91048c61fe34e12fa7a93af27f9" 12 | 13 | [genesis] 14 | validator = ["0x7193d8f91724b39f10cc81e94934c187fa257277", "0x93908f59c6eff007d228398349214acb6b4ac9a4", "0x72d5c75fd6703414aa87f79b3e4797dd09cd9251", "0x58096d35c7a8ff67eba159f33cea7740fc9a737c","0xc759616c865d349ec2afced268fc6f33ff7414a4"] 15 | epoch_time = 2018-09-09T09:09:09.09-09:09 16 | proposer = "0x5701fbd05e77cac003a6894e4b2a3c12287ed313" 17 | gas_used = 10000 18 | extra = "Hello Word!" 19 | [genesis.accounts] 20 | 0x5701fbd05e77cac003a6894e4b2a3c12287ed313 = 500000 21 | 0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d = 500000 22 | 0x3140bda54df92f9453b487afdb3bcce02d154c74 = 500000 23 | 0x7035dafbeac1792ab5b7ed5c903ac63522eb534a = 500000 24 | 0x6730933a2cb6f26af786d7f5979efbdf29049c3a = 500000 25 | 0xfb1bbe89190c9793aec79713e35bdd82a7e5b08b = 500000 26 | 0x1f6f0d11339b5a0db7cef22ae278c15d55178faf = 500000 27 | 0x17f3309f405f53ae3e3c7e98533c58aa0c8c9417 = 500000 28 | 0x6e6e4a7aa7cedac4c4f3e8a7cd363e5f3208e8a6 = 500000 29 | 0x91b73cc738754c4fd7d6a2f0b6b354e293177c80 = 500000 30 | -------------------------------------------------------------------------------- /examples/c3.toml: -------------------------------------------------------------------------------- 1 | chain_id = 10 2 | ip = "127.0.0.1" 3 | port = 7693 4 | api_ip = "0.0.0.0" 5 | api_port = 8693 6 | block_period = 1000 # ms 7 | request_time = 5000 # ms 8 | peer_id = "QmZREuJee6NMFpYGroctffcwUVDAYmt3mB1eisSVqVVfwA" 9 | ttl = 3000 10 | store = "/tmp/block/c3" 11 | secret = "64115814914b9d1aaa7d485770f50274b673df4634fcdd0ea3347e73e4b800ad" 12 | 13 | [genesis] 14 | validator = ["0x7193d8f91724b39f10cc81e94934c187fa257277", "0x93908f59c6eff007d228398349214acb6b4ac9a4", "0x72d5c75fd6703414aa87f79b3e4797dd09cd9251", "0x58096d35c7a8ff67eba159f33cea7740fc9a737c","0xc759616c865d349ec2afced268fc6f33ff7414a4"] 15 | epoch_time = 2018-09-09T09:09:09.09-09:09 16 | proposer = "0x5701fbd05e77cac003a6894e4b2a3c12287ed313" 17 | gas_used = 10000 18 | extra = "Hello Word!" 19 | [genesis.accounts] 20 | 0x5701fbd05e77cac003a6894e4b2a3c12287ed313 = 500000 21 | 0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d = 500000 22 | 0x3140bda54df92f9453b487afdb3bcce02d154c74 = 500000 23 | 0x7035dafbeac1792ab5b7ed5c903ac63522eb534a = 500000 24 | 0x6730933a2cb6f26af786d7f5979efbdf29049c3a = 500000 25 | 0xfb1bbe89190c9793aec79713e35bdd82a7e5b08b = 500000 26 | 0x1f6f0d11339b5a0db7cef22ae278c15d55178faf = 500000 27 | 0x17f3309f405f53ae3e3c7e98533c58aa0c8c9417 = 500000 28 | 0x6e6e4a7aa7cedac4c4f3e8a7cd363e5f3208e8a6 = 500000 29 | 0x91b73cc738754c4fd7d6a2f0b6b354e293177c80 = 500000 30 | -------------------------------------------------------------------------------- /examples/c4.toml: -------------------------------------------------------------------------------- 1 | chain_id = 10 2 | ip = "127.0.0.1" 3 | port = 7694 4 | api_ip = "0.0.0.0" 5 | api_port = 8694 6 | block_period = 1000 # ms 7 | request_time = 5000 # ms 8 | peer_id = "QmVQVbmHE2BwFjeKx4eAKwGBtZ5348ufVUUR8jG2pa8uhG" 9 | ttl = 3000 10 | store = "/tmp/block/c4" 11 | secret = "f9093897ce74d867cdbc5c5a1b6e840ffb4343cbb0ea5b3ad5525edc6bad8c95" 12 | 13 | [genesis] 14 | validator = ["0x7193d8f91724b39f10cc81e94934c187fa257277", "0x93908f59c6eff007d228398349214acb6b4ac9a4", "0x72d5c75fd6703414aa87f79b3e4797dd09cd9251", "0x58096d35c7a8ff67eba159f33cea7740fc9a737c","0xc759616c865d349ec2afced268fc6f33ff7414a4"] 15 | epoch_time = 2018-09-09T09:09:09.09-09:09 16 | proposer = "0x5701fbd05e77cac003a6894e4b2a3c12287ed313" 17 | gas_used = 10000 18 | extra = "Hello Word!" 19 | [genesis.accounts] 20 | 0x5701fbd05e77cac003a6894e4b2a3c12287ed313 = 500000 21 | 0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d = 500000 22 | 0x3140bda54df92f9453b487afdb3bcce02d154c74 = 500000 23 | 0x7035dafbeac1792ab5b7ed5c903ac63522eb534a = 500000 24 | 0x6730933a2cb6f26af786d7f5979efbdf29049c3a = 500000 25 | 0xfb1bbe89190c9793aec79713e35bdd82a7e5b08b = 500000 26 | 0x1f6f0d11339b5a0db7cef22ae278c15d55178faf = 500000 27 | 0x17f3309f405f53ae3e3c7e98533c58aa0c8c9417 = 500000 28 | 0x6e6e4a7aa7cedac4c4f3e8a7cd363e5f3208e8a6 = 500000 29 | 0x91b73cc738754c4fd7d6a2f0b6b354e293177c80 = 500000 30 | -------------------------------------------------------------------------------- /examples/c5.toml: -------------------------------------------------------------------------------- 1 | chain_id = 10 2 | ip = "127.0.0.1" 3 | port = 7695 4 | api_ip = "0.0.0.0" 5 | api_port = 8695 6 | block_period = 1000 # ms 7 | request_time = 5000 # ms 8 | peer_id = "QmX5e9hkQf7B45e2MZf38vhsC2wfA5aKQrrBuLujwaUBGw" 9 | ttl = 3000 10 | store = "/tmp/block/c5" 11 | secret = "6a30cfa9d15d64e4d7b0f15a18d6ea78d242e820e012b9980af5dbdc6403f61a" 12 | 13 | [genesis] 14 | validator = ["0x7193d8f91724b39f10cc81e94934c187fa257277", "0x93908f59c6eff007d228398349214acb6b4ac9a4", "0x72d5c75fd6703414aa87f79b3e4797dd09cd9251", "0x58096d35c7a8ff67eba159f33cea7740fc9a737c","0xc759616c865d349ec2afced268fc6f33ff7414a4"] 15 | epoch_time = 2018-09-09T09:09:09.09-09:09 16 | proposer = "0x5701fbd05e77cac003a6894e4b2a3c12287ed313" 17 | gas_used = 10000 18 | extra = "Hello Word!" 19 | [genesis.accounts] 20 | 0x5701fbd05e77cac003a6894e4b2a3c12287ed313 = 500000 21 | 0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d = 500000 22 | 0x3140bda54df92f9453b487afdb3bcce02d154c74 = 500000 23 | 0x7035dafbeac1792ab5b7ed5c903ac63522eb534a = 500000 24 | 0x6730933a2cb6f26af786d7f5979efbdf29049c3a = 500000 25 | 0xfb1bbe89190c9793aec79713e35bdd82a7e5b08b = 500000 26 | 0x1f6f0d11339b5a0db7cef22ae278c15d55178faf = 500000 27 | 0x17f3309f405f53ae3e3c7e98533c58aa0c8c9417 = 500000 28 | 0x6e6e4a7aa7cedac4c4f3e8a7cd363e5f3208e8a6 = 500000 29 | 0x91b73cc738754c4fd7d6a2f0b6b354e293177c80 = 500000 30 | -------------------------------------------------------------------------------- /examples/c6.toml: -------------------------------------------------------------------------------- 1 | chain_id = 10 2 | ip = "127.0.0.1" 3 | port = 7696 4 | api_ip = "0.0.0.0" 5 | api_port = 8696 6 | block_period = 1000 # ms 7 | request_time = 5000 # ms 8 | peer_id = "QmZgaZTxwWRWHbH6sukyFWAaNwPKvZGX9m7epEHGBJHPvt" 9 | ttl = 3000 10 | store = "/tmp/block/c6" 11 | secret = "ec84caf3d58e6bbcdcd6b243203fbaafee19e91048c61fe34e12fa7a93af27f9" 12 | 13 | [genesis] 14 | validator = ["0x7193d8f91724b39f10cc81e94934c187fa257277", "0x93908f59c6eff007d228398349214acb6b4ac9a4", "0x72d5c75fd6703414aa87f79b3e4797dd09cd9251", "0x58096d35c7a8ff67eba159f33cea7740fc9a737c","0xc759616c865d349ec2afced268fc6f33ff7414a4"] 15 | epoch_time = 2018-09-09T09:09:09.09-09:09 16 | proposer = "0x0701fbd05e77cac003a6894e4b2a3c12287ed313" 17 | gas_used = 10000 18 | extra = "Hello Word!" 19 | [genesis.accounts] 20 | 0x5701fbd05e77cac003a6894e4b2a3c12287ed313 = 500000 21 | 0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d = 500000 22 | 0x3140bda54df92f9453b487afdb3bcce02d154c74 = 500000 23 | 0x7035dafbeac1792ab5b7ed5c903ac63522eb534a = 500000 24 | 0x6730933a2cb6f26af786d7f5979efbdf29049c3a = 500000 25 | 0xfb1bbe89190c9793aec79713e35bdd82a7e5b08b = 500000 26 | 0x1f6f0d11339b5a0db7cef22ae278c15d55178faf = 500000 27 | 0x17f3309f405f53ae3e3c7e98533c58aa0c8c9417 = 500000 28 | 0x6e6e4a7aa7cedac4c4f3e8a7cd363e5f3208e8a6 = 500000 29 | 0x91b73cc738754c4fd7d6a2f0b6b354e293177c80 = 500000 30 | -------------------------------------------------------------------------------- /examples/config.toml: -------------------------------------------------------------------------------- 1 | chain_id = 10 2 | ip = "127.0.0.1" 3 | port = 7690 4 | block_period = 10000 # ms 5 | request_time = 5000 # ms 6 | peer_id = "QmbBr2fHwLFKvHkAq1BpbEr4dvR8P6orQxHkVaxeJsJiW8" 7 | ttl = 3000 8 | store = "/tmp/block/c0" 9 | secret = "6a30cfa9d15d64e4d7b0f15a18d6ea78d242e820e012b9980af5dbdc6403f61a" 10 | 11 | [genesis] 12 | validator = ["0x5701fbd05e77cac003a6894e4b2a3c12287ed313", "0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d", "0x3140bda54df92f9453b487afdb3bcce02d154c74", "0x7035dafbeac1792ab5b7ed5c903ac63522eb534a","0x6730933a2cb6f26af786d7f5979efbdf29049c3a"] 13 | epoch_time = 2018-09-09T09:09:09.09-09:09 14 | proposer = "0x5701fbd05e77cac003a6894e4b2a3c12287ed313" 15 | gas_used = 10000 16 | extra = "Hello Word!" 17 | [genesis.accounts] 18 | 0x5701fbd05e77cac003a6894e4b2a3c12287ed313 = 500000 19 | 0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d = 500000 20 | 0x3140bda54df92f9453b487afdb3bcce02d154c74 = 500000 21 | 0x7035dafbeac1792ab5b7ed5c903ac63522eb534a = 500000 22 | 0x6730933a2cb6f26af786d7f5979efbdf29049c3a = 500000 23 | 0xfb1bbe89190c9793aec79713e35bdd82a7e5b08b = 500000 24 | 0x1f6f0d11339b5a0db7cef22ae278c15d55178faf = 500000 25 | 0x17f3309f405f53ae3e3c7e98533c58aa0c8c9417 = 500000 26 | 0x6e6e4a7aa7cedac4c4f3e8a7cd363e5f3208e8a6 = 500000 27 | 0x91b73cc738754c4fd7d6a2f0b6b354e293177c80 = 500000 -------------------------------------------------------------------------------- /examples/docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dir=`pwd` && docker run -ti --rm -v $dir/examples/:/data/ -v $dir/docker_start.sh:/data/docker_start.sh zhrong/bft /data/docker_start.sh && tail -f /tmp/c2.log 4 | -------------------------------------------------------------------------------- /examples/docker_start.sh: -------------------------------------------------------------------------------- 1 | RUST_BACKTRACE=full RUST_LOG=info nohup /root/release/examples/bft start -c /data/c1.toml 1> /tmp/c1.log 2>&1 & 2 | RUST_BACKTRACE=full RUST_LOG=info nohup /root/release/examples/bft start -c /data/c2.toml 1> /tmp/c2.log 2>&1 & 3 | RUST_BACKTRACE=full RUST_LOG=info nohup /root/release/examples/bft start -c /data/c3.toml 1> /tmp/c3.log 2>&1 & 4 | RUST_BACKTRACE=full RUST_LOG=info nohup /root/release/examples/bft start -c /data/c4.toml 1> /tmp/c4.log 2>&1 & 5 | RUST_BACKTRACE=full RUST_LOG=info nohup /root/release/examples/bft start -c /data/c5.toml 1> /tmp/c5.log 2>&1 & 6 | -------------------------------------------------------------------------------- /examples/p2p.rs: -------------------------------------------------------------------------------- 1 | extern crate bft; 2 | extern crate futures; 3 | extern crate libp2p; 4 | extern crate rand; 5 | extern crate tokio; 6 | 7 | use futures::prelude::*; 8 | use libp2p::mdns::{MdnsPacket, MdnsService}; 9 | use libp2p::PeerId; 10 | use libp2p::core::PublicKey; 11 | use libp2p::multiaddr::{Multiaddr, ToMultiaddr}; 12 | use std::io; 13 | use rand::Rng; 14 | use std::time::Duration; 15 | 16 | fn main() { 17 | let mut service = MdnsService::new().expect("Error while creating mDNS service"); 18 | let my_peer_id = PeerId::random(); 19 | let mut my_listened_addrs = Vec::new(); 20 | println!("my pid {:?}", my_peer_id); 21 | let port = rand::random::(); 22 | let address: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port).parse().unwrap(); 23 | 24 | 25 | my_listened_addrs.push(address); 26 | let future = futures::future::poll_fn(move || -> Poll<(), io::Error> { 27 | loop { 28 | // Grab the next available packet from the service. 29 | let packet = match service.poll() { 30 | Async::Ready(packet) => packet, 31 | Async::NotReady => return Ok(Async::NotReady), 32 | }; 33 | 34 | match packet { 35 | MdnsPacket::Query(query) => { 36 | // We detected a libp2p mDNS query on the network. In a real application, you 37 | // probably want to answer this query by doing `query.respond(...)`. 38 | println!("Detected query from {:?}", query.remote_addr()); 39 | query.respond(my_peer_id.clone(), my_listened_addrs.clone(), Duration::from_secs(3)).unwrap(); 40 | } 41 | MdnsPacket::Response(response) => { 42 | // We detected a libp2p mDNS response on the network. Responses are for 43 | // everyone and not just for the requester, which makes it possible to 44 | // passively listen. 45 | for peer in response.discovered_peers() { 46 | println!("Discovered peer {:?}", peer.id()); 47 | // These are the self-reported addresses of the peer we just discovered. 48 | for addr in peer.addresses() { 49 | println!(" Address = {:?}", addr); 50 | } 51 | } 52 | } 53 | MdnsPacket::ServiceDiscovery(query) => { 54 | // The last possibility is a service detection query from DNS-SD. 55 | // Just like `Query`, in a real application you probably want to call 56 | // `query.respond`. 57 | println!("Detected service query from {:?}", query.remote_addr()); 58 | query.respond(std::time::Duration::from_secs(120)); 59 | } 60 | } 61 | } 62 | }); 63 | 64 | // Blocks the thread until the future runs to completion (which will never happen). 65 | tokio::run(future.map_err(|err| panic!("{:?}", err))); 66 | } 67 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | write_mode = "overwrite" -------------------------------------------------------------------------------- /src/api/mod.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::core::chain::Chain; 4 | use crate::types::block::Blocks; 5 | 6 | use http::StatusCode; 7 | use tide::{body, head, configuration::{Configuration, Environment}, App, AppData}; 8 | 9 | async fn blocks(mut chain: AppData>) -> String { 10 | let state: &Arc = &chain.0; 11 | let last_height = state.get_last_height(); 12 | let mut blocks: Blocks = Blocks(vec![]); 13 | (0..last_height + 1).for_each(|height| { 14 | let block_hash = state.get_block_hash_by_height(height).unwrap(); 15 | let block = state.get_block_by_hash(&block_hash).unwrap(); 16 | blocks.0.push(block); 17 | }); 18 | serde_json::to_string(&blocks).unwrap() 19 | } 20 | 21 | async fn transactions(mut chain: AppData>) -> String { 22 | let state: &Arc = &chain.0; 23 | let mut transactions = state.get_transactions(); 24 | serde_json::to_string(&transactions).unwrap() 25 | } 26 | 27 | pub fn start_api(chain: Arc, ip: String, port: u16) { 28 | let mut app = App::new(chain); 29 | app.at("/blocks").get(blocks); 30 | app.at("/transactions").get(transactions); 31 | app.config(Configuration { 32 | env: Environment::Production, 33 | address: ip, 34 | port: port, 35 | }); 36 | app.serve(); 37 | } -------------------------------------------------------------------------------- /src/cmd/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{self, prelude::*}; 3 | use std::str::FromStr; 4 | use std::sync::mpsc::Sender; 5 | use std::sync::Arc; 6 | use std::thread::{spawn, JoinHandle}; 7 | use std::time::Duration; 8 | 9 | use ::actix::prelude::*; 10 | use cryptocurrency_kit::crypto::Hash; 11 | use cryptocurrency_kit::ethkey::{Generator, KeyPair, Secret, Random}; 12 | use futures::Future; 13 | use kvdb_rocksdb::Database; 14 | use libp2p::{Multiaddr, PeerId}; 15 | use lru_time_cache::LruCache; 16 | use parking_lot::RwLock; 17 | 18 | use crate::{ 19 | common, 20 | config::Config, 21 | consensus::pbft::core::core::{Core, handle_msg_middle}, 22 | consensus::consensus::{create_bft_engine, SafeEngine}, 23 | core::chain::Chain, 24 | core::ledger::{LastMeta, Ledger}, 25 | core::tx_pool::{BaseTxPool, TxPool, SafeTxPool}, 26 | error::ChainResult, 27 | logger::init_log, 28 | minner::Minner, 29 | p2p::{ 30 | protocol::Payload, 31 | discover_service::DiscoverService, 32 | server::{author_handshake, TcpServer}, 33 | spawn_sync_subscriber, 34 | }, 35 | pprof::spawn_signal_handler, 36 | store::schema::Schema, 37 | subscriber::events::{BroadcastEventSubscriber, ChainEventSubscriber, SubscriberType}, 38 | subscriber::*, 39 | types::Validator, 40 | api::start_api, 41 | }; 42 | 43 | pub fn start_node(config: &str, sender: Sender<()>) -> Result<(), String> { 44 | print_art(); 45 | init_log(); 46 | let result = init_config(config); 47 | if result.is_err() { 48 | return Err(result.err().unwrap()); 49 | } 50 | let config = result.unwrap(); 51 | let secret = Secret::from_str(&config.secret).expect("Secret is uncorrect"); 52 | let key_pair = KeyPair::from_secret(secret).unwrap(); 53 | let ledger = init_store(&config)?; 54 | let ledger: Arc> = Arc::new(RwLock::new(ledger)); 55 | 56 | let mut chain = Chain::new(config.clone(), ledger); 57 | 58 | // init genesis 59 | init_genesis(&mut chain).map_err(|err| format!("{}", err))?; 60 | let genesis = chain.get_genesis().clone(); 61 | info!("Genesis hash: {:?}", chain.get_genesis().hash()); 62 | 63 | // init transaction pool 64 | let _tx_pool = Arc::new(RwLock::new(init_transaction_pool(&config))); 65 | 66 | let chain = Arc::new(chain); 67 | 68 | init_api(&config, chain.clone()); 69 | 70 | let broadcast_subscriber = BroadcastEventSubscriber::new(SubscriberType::Async).start(); 71 | 72 | let (core_pid, engine) = start_consensus_engine( 73 | &config, 74 | key_pair.clone(), 75 | chain.clone(), 76 | broadcast_subscriber.clone(), 77 | ); 78 | 79 | let config_clone = config.clone(); 80 | { 81 | let p2p_event_notify = init_p2p_event_notify(); 82 | let _discover_pid = init_p2p_service(p2p_event_notify.clone(), &config_clone); 83 | init_tcp_server(chain.clone(), p2p_event_notify.clone(), genesis.hash(), core_pid.clone(), &config_clone); 84 | } 85 | 86 | // spawn new thread to handle mine 87 | ::std::thread::spawn(move || { 88 | let code = System::run(move || { 89 | start_mint(&config, key_pair.clone(), chain.clone(), _tx_pool.clone(), engine); 90 | }); 91 | ::std::process::exit(code); 92 | }); 93 | 94 | init_signal_handle(); 95 | Ok(()) 96 | } 97 | 98 | fn init_p2p_event_notify() -> Addr { 99 | info!("Init p2p event nofity"); 100 | spawn_sync_subscriber() 101 | } 102 | 103 | fn init_p2p_service( 104 | p2p_subscriber: Addr, 105 | config: &Config, 106 | ) -> Addr { 107 | let peer_id = PeerId::from_str(&config.peer_id).unwrap(); 108 | let mul_addr = Multiaddr::from_str(&format!("/ip4/{}/tcp/{}", config.ip, config.port)).unwrap(); 109 | let discover_service = 110 | DiscoverService::spawn_discover_service(p2p_subscriber, peer_id, mul_addr, config.ttl); 111 | info!("Init p2p service successfully"); 112 | discover_service 113 | } 114 | 115 | fn init_tcp_server(chain: Arc, p2p_subscriber: Addr, genesis: Hash, core_pid: Addr, config: &Config) { 116 | let peer_id = PeerId::from_str(&config.peer_id).unwrap(); 117 | let mul_addr = Multiaddr::from_str(&format!("/ip4/{}/tcp/{}", config.ip, config.port)).unwrap(); 118 | let author = author_handshake(genesis.clone()); 119 | let h1 = Box::new(handle_msg_middle(core_pid, chain.clone())); 120 | let server = TcpServer::new(peer_id, mul_addr, None, genesis.clone(), Box::new(author), h1); 121 | 122 | // subscriber p2p event, sync operation 123 | { 124 | let recipient = server.clone().recipient(); 125 | // register 126 | let message = SubscribeMessage::SubScribe(recipient); 127 | let request_fut = p2p_subscriber.send(message); 128 | Arbiter::spawn( 129 | request_fut 130 | .and_then(|_result| { 131 | info!("Subsribe p2p discover event successfully"); 132 | futures::future::ok(()) 133 | }) 134 | .map_err(|err| unimplemented!("{}", err)), 135 | ); 136 | } 137 | 138 | // subscriber chain event, async operation 139 | { 140 | chain.subscriber_event(server.clone().recipient()); 141 | } 142 | info!("Init tcp server successfully"); 143 | } 144 | 145 | fn init_config(config: &str) -> Result { 146 | info!("Init config: {}", config); 147 | let mut input = String::new(); 148 | File::open(config) 149 | .and_then(|mut f| f.read_to_string(&mut input)) 150 | .map(|_| toml::from_str::(&input).unwrap()) 151 | .map_err(|err| err.to_string()) 152 | } 153 | 154 | fn init_transaction_pool(_: &Config) -> SafeTxPool { 155 | info!("Init transaction pool successfully"); 156 | Box::new(BaseTxPool::new()) as SafeTxPool 157 | } 158 | 159 | fn init_store(config: &Config) -> Result { 160 | info!("Init store: {}", config.store); 161 | let genesis_config = config.genesis.as_ref().unwrap(); 162 | 163 | let mut validators: Vec = vec![]; 164 | for validator in &genesis_config.validator { 165 | validators.push(Validator::new(common::string_to_address(validator)?)); 166 | } 167 | 168 | let database = Database::open_default(&config.store).map_err(|err| err.to_string())?; 169 | let schema = Schema::new(Arc::new(database)); 170 | Ok(Ledger::new( 171 | LastMeta::new_zero(), 172 | LruCache::with_capacity(1 << 10), 173 | LruCache::with_capacity(1 << 10), 174 | validators, 175 | schema, 176 | )) 177 | } 178 | 179 | fn init_genesis(chain: &mut Chain) -> ChainResult { 180 | info!("Init genesis block"); 181 | chain.store_genesis_block() 182 | } 183 | 184 | fn start_consensus_engine( 185 | _config: &Config, 186 | key_pair: KeyPair, 187 | chain: Arc, 188 | subscriber: Addr, 189 | ) -> (Addr, SafeEngine) { 190 | info!("Init consensus engine"); 191 | let mut result = create_bft_engine(key_pair, chain, subscriber); 192 | result.1.start().unwrap(); 193 | result 194 | } 195 | 196 | fn start_mint( 197 | config: &Config, 198 | key_pair: KeyPair, 199 | chain: Arc, 200 | txpool: Arc>, 201 | engine: SafeEngine, 202 | ) -> Addr { 203 | let minter = key_pair.address(); 204 | Minner::create(move |ctx| { 205 | let recipient = ctx.address().recipient(); 206 | chain.subscriber_event(recipient); 207 | let (tx, rx) = crossbeam::channel::bounded(1); 208 | Minner::new(minter, key_pair, chain, txpool, engine, tx, rx) 209 | }) 210 | } 211 | 212 | fn init_api(config: &Config, chain: Arc) { 213 | let config = config.clone(); 214 | let chain = chain.clone(); 215 | spawn(move || { 216 | info!("Start service api"); 217 | start_api(chain, config.api_ip, config.api_port); 218 | }); 219 | } 220 | 221 | fn init_signal_handle() { 222 | spawn_signal_handler(*common::random_dir()); 223 | } 224 | 225 | fn print_art() { 226 | let art = r#" 227 | A large collection of ASCII art drawings of bears and other related animal ASCII art pictures. 228 | 229 | lazy bears by Joan G. Stark 230 | 231 | _,-""`""-~`) 232 | (`~_,=========\ 233 | |---,___.-.__,\ 234 | | o \ ___ _,,,,_ _.--. 235 | \ `^` /`_.-"~ `~-;` \ 236 | \_ _ .' `, | 237 | |`- \'__/ 238 | / ,_ \ `'-. 239 | / .-""~~--. `"-, ;_ / 240 | | \ \ | `""` 241 | \__.--'`"-. /_ |' 242 | `"` `~~~---.., | 243 | \ _.-'`-. 244 | "#; 245 | println!("{}", art); 246 | } 247 | -------------------------------------------------------------------------------- /src/common/mod.rs: -------------------------------------------------------------------------------- 1 | use bigint::U256; 2 | use rand::random; 3 | use sha3::{Digest, Sha3_256}; 4 | 5 | use core::str::FromStr; 6 | use std::env; 7 | use std::fmt::{self, Display}; 8 | use std::net::{SocketAddr, AddrParseError}; 9 | 10 | use cryptocurrency_kit::crypto::{hash, CryptoHash, Hash}; 11 | use cryptocurrency_kit::merkle_tree::MerkleTree; 12 | use cryptocurrency_kit::storage::values::StorageValue; 13 | use libp2p::{ 14 | multiaddr::Protocol, 15 | Multiaddr, 16 | }; 17 | 18 | pub fn merkle_tree_root(input: Vec) -> Hash { 19 | let mut v: Vec> = vec![]; 20 | for item in input { 21 | let bytes = item.into_bytes(); 22 | v.push(bytes); 23 | } 24 | let root = MerkleTree::new_merkle_tree(v).root.unwrap(); 25 | // info!("{:?}, len: {}", &root.data); 26 | Hash::from_slice(&root.data).unwrap() 27 | } 28 | 29 | #[derive(Serialize, Deserialize, Debug, Clone)] 30 | pub struct HexBytes { 31 | inner: [u8; 32], 32 | } 33 | 34 | impl HexBytes { 35 | pub fn bytes(&self) -> &[u8; 32] { 36 | &self.inner 37 | } 38 | 39 | pub fn string(&self) -> String { 40 | String::from_utf8_lossy(&self.inner).to_string() 41 | } 42 | } 43 | 44 | impl Display for HexBytes { 45 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 46 | use std::fmt; 47 | writeln!(f, "{}", self.string()).unwrap(); 48 | Ok(()) 49 | } 50 | } 51 | 52 | pub fn as_256(data: &[u8]) -> U256 { 53 | U256::from_big_endian(data) 54 | } 55 | 56 | pub fn u256_hash(input: &[u8]) -> Vec { 57 | let mut hasher = Sha3_256::default(); 58 | hasher.input(input); 59 | hasher.result().to_vec() 60 | } 61 | 62 | pub fn random_dir() -> Box { 63 | Box::new(format!( 64 | "{}{}", 65 | env::temp_dir().to_str().unwrap(), 66 | random::() 67 | )) 68 | } 69 | 70 | 71 | pub fn multiaddr_to_ipv4(mul_addr: &Multiaddr) -> Result { 72 | let mut ipv4: String = "".to_string(); 73 | let v = mul_addr.iter().collect::>(); 74 | for protocol in v { 75 | match protocol { 76 | Protocol::Ip4(ref ip4) => { 77 | ipv4.push_str(&format!("{}:", ip4)); 78 | } 79 | Protocol::Tcp(ref port) => { 80 | ipv4.push_str(&format!("{}", port)); 81 | } 82 | _ => {} 83 | } 84 | } 85 | ipv4.parse() 86 | } 87 | 88 | pub fn random_uuid() -> uuid::Uuid { 89 | use uuid::Uuid; 90 | Uuid::new_v5(&Uuid::NAMESPACE_DNS, chrono::Local::now().to_string().as_bytes()) 91 | } 92 | 93 | 94 | use ethereum_types::{Address, H160}; 95 | 96 | pub fn string_to_address(s: &String) -> Result { 97 | if s.len() < 40 { 98 | return Err("less than 40 chars".to_string()); 99 | } 100 | if s.len() > 42 { 101 | return Err("more than 42 chars".to_string()); 102 | } 103 | 104 | if s.len() == 42 { 105 | return Ok(Address::from_str(&s[2..]).unwrap()); 106 | } 107 | Ok(Address::from_str(&s).unwrap()) 108 | } 109 | 110 | pub fn strings_to_addresses(strs: &Vec) -> Result, String> { 111 | let mut addresses = Vec::new(); 112 | for str in strs { 113 | let address = string_to_address(str)?; 114 | addresses.push(address); 115 | } 116 | Ok(addresses) 117 | } 118 | 119 | #[cfg(test)] 120 | mod test { 121 | use super::*; 122 | 123 | #[test] 124 | fn t_string_to_address() { 125 | let address = string_to_address(&"0x93908f59c6eff007d228398349214acb6b4ac9a4".to_owned()).unwrap(); 126 | assert_eq!("0x93908f59c6eff007d228398349214acb6b4ac9a4", format!("{:?}", address)); 127 | println!("address: {:?}", address); 128 | } 129 | } -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use std::collections::HashMap; 3 | 4 | use toml::Value as Toml; 5 | use toml::value::Table; 6 | use toml::value::Datetime; 7 | 8 | use crate::common::random_dir; 9 | 10 | #[derive(Debug, Clone, Deserialize)] 11 | pub struct Config { 12 | pub chain_id: u64, 13 | pub ip: String, 14 | pub port: u16, 15 | pub api_ip: String, 16 | pub api_port: u16, 17 | #[serde(with = "serde_millis")] 18 | pub block_period: Duration, 19 | #[serde(with = "serde_millis")] 20 | pub request_time: Duration, 21 | pub peer_id: String, 22 | #[serde(with = "serde_millis")] 23 | pub ttl: Duration, 24 | pub store: String, 25 | pub secret: String, 26 | pub genesis: Option, 27 | } 28 | 29 | #[derive(Debug, Deserialize, Clone)] 30 | pub struct GenesisConfig { 31 | pub validator: Vec, 32 | pub accounts: Table, 33 | pub epoch_time: Datetime, 34 | pub proposer: String, 35 | pub gas_used: u64, 36 | pub extra: String, 37 | } 38 | 39 | impl Default for Config { 40 | fn default() -> Self { 41 | Config { 42 | chain_id: 98, 43 | ip: "127.0.0.1".to_string(), 44 | port: 7960, 45 | api_ip: "0.0.0.0".to_owned(), 46 | api_port: 8960, 47 | block_period: Duration::from_millis(3 * 1000), 48 | request_time: Duration::from_millis(3 * 1000), 49 | peer_id: "QmbBr2fHwLFKvHkAq1BpbEr4dvR8P6orQxHkVaxeJsJiW8".to_string(), 50 | ttl: Duration::from_millis(5 * 1000), 51 | store: *random_dir(), 52 | secret: "".into(), 53 | genesis: None, 54 | } 55 | } 56 | } 57 | 58 | #[cfg(test)] 59 | mod tests { 60 | use super::*; 61 | use libp2p::PeerId; 62 | use std::str::FromStr; 63 | 64 | #[test] 65 | fn t_config() { 66 | println!("{:?}", PeerId::random()); 67 | println!("{:?}", PeerId::from_str("QmbBr2fHwLFKvHkAq1BpbEr4dvR8P6orQxHkVaxeJsJiW8").unwrap()); 68 | } 69 | 70 | #[test] 71 | fn t_load_secret(){ 72 | use cryptocurrency_kit::ethkey::{Secret, KeyPair}; 73 | 74 | let secret = Secret::from_str("7f3b0a324e13e5358c3fd686737acd7adf2e5556084ec6d9e48b497082b7ef98").unwrap(); 75 | let key_pair = KeyPair::from_secret(secret).unwrap(); 76 | println!("{:?}, {:?}", key_pair, key_pair.address()); 77 | } 78 | } -------------------------------------------------------------------------------- /src/consensus/config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub struct Config { 3 | pub request_time: u64, 4 | pub block_period: u64, 5 | pub chain_id: u64, 6 | } 7 | 8 | impl Config { 9 | pub fn new(request_time:u64, block_period: u64, chain_id: u64) -> Self { 10 | Config{ 11 | request_time, 12 | block_period, 13 | chain_id, 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/consensus/consensus.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use actix::Addr; 4 | use cryptocurrency_kit::ethkey::Address; 5 | use cryptocurrency_kit::ethkey::KeyPair; 6 | use crossbeam::Receiver; 7 | 8 | use super::{ 9 | error::{EngineError, EngineResult}, 10 | types::Proposal, 11 | pbft::core::core::Core, 12 | backend::{Backend, ImplBackend, new_impl_backend}, 13 | validator::ImplValidatorSet, 14 | }; 15 | 16 | use crate::{ 17 | subscriber::events::{BroadcastEvent, BroadcastEventSubscriber}, 18 | types::block::{Block, Header}, 19 | core::chain::Chain, 20 | consensus::events::OpCMD, 21 | }; 22 | 23 | struct BftConfig { 24 | request_time: u64, 25 | block_period: u64, 26 | } 27 | 28 | pub trait Engine { 29 | fn start(&mut self) -> Result<(), String>; 30 | fn stop(&mut self) -> Result<(), String>; 31 | fn author(&self, header: &Header) -> Result; 32 | fn verify_header(&self, header: &Header, seal: bool) -> EngineResult; 33 | fn verify_seal(&self, header: &Header) -> EngineResult; 34 | fn new_chain_header(&mut self, proposal: &Proposal) -> EngineResult; 35 | fn prepare(&mut self, header: &mut Header) -> Result<(), String>; 36 | fn finalize(&mut self, header: &Header) -> Result<(), String>; 37 | fn seal(&mut self, new_block: &mut Block, abort: Receiver<()>) -> EngineResult; 38 | } 39 | 40 | pub type SafeEngine = Box; 41 | 42 | pub fn create_bft_engine(key_pair: KeyPair, chain: Arc, subscriber: Addr) -> (Addr, SafeEngine) { 43 | info!("Create bft consensus engine"); 44 | let mut backend = new_impl_backend(key_pair.clone(), chain.clone(), subscriber); 45 | 46 | // use new thread to handle core 47 | let (tx, rx) = ::std::sync::mpsc::channel(); 48 | let core_backend = backend.clone(); 49 | ::std::thread::spawn(move || { 50 | let core = actix::System::run(move || { 51 | let core_pid = Core::new(chain, core_backend, key_pair); 52 | tx.send(core_pid).unwrap(); 53 | }); 54 | ::std::process::exit(core); 55 | }); 56 | let core_pid = rx.recv().unwrap(); 57 | backend.set_core_pid(core_pid.clone()); 58 | let engine_backend: SafeEngine = Box::new(backend.clone()) as SafeEngine; 59 | (core_pid, engine_backend) 60 | } 61 | -------------------------------------------------------------------------------- /src/consensus/dpos/delegates.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{Height, transaction::Transaction, block::Block}; 2 | 3 | use super::{util, slot}; 4 | 5 | // TODO: add_delegate 6 | fn add_delegate() {} 7 | 8 | /// Generates delegates list and checks if block generator publicKey maches delegate id. 9 | pub fn validate_block_slot(block: Block) -> bool { 10 | // get the height delegates list 11 | let block_height = block.height(); 12 | let delegates = slot::get_active_delegates(block_height); 13 | let slot_time = block.header().time as i64; 14 | 15 | let current_slot = slot::get_slot_number(slot_time); 16 | let idx = current_slot as usize % delegates.len(); 17 | let delegate_id = delegates[idx]; 18 | 19 | util::equal_delegate(delegate_id, &block.header().proposer.as_bytes()) 20 | } 21 | 22 | 23 | //// TODO: Opz 24 | //pub fn generate_delegate_list<'a>(height: Height) -> (Timespec, Vec<&'a str>){ 25 | // let generator_id = slot::get_active_delegates(height); 26 | //} 27 | 28 | pub fn get_block_slot_data<'a>(slot: i64, height: Height) -> Option<(&'a str, i64)> { 29 | let current_slot = slot; 30 | let delegates = slot::get_active_delegates(height); 31 | let delegate_pos = current_slot % slot::DELEGATES; 32 | let delegate_id = delegates.get(delegate_pos as usize); 33 | Some((delegate_id.unwrap(), slot::get_slot_time(slot))) 34 | } 35 | 36 | // TODO: 37 | // height --> slot_list 38 | // 根据当前的slot,找到高度为height的相应见证人id以及相对应的slot 39 | // 40 | //fn get_block_slot_data<'a>(slot: i64, height: Height) -> Option<(&'a str, i64)> { 41 | // let current_slot = slot; 42 | // let last_slot = slot::get_last_slot(current_slot); 43 | // let delegates = slot::get_active_delegates(height); 44 | // 45 | // for _slot in current_slot..last_slot { 46 | // let delegate_pos = _slot % slot::DELEGATES; 47 | // let deletegate_id = delegates.get(delegate_pos as usize); 48 | // if deletegate_id.is_none() { 49 | // continue; 50 | // } 51 | // return Some((deletegate_id.unwrap(), slot::get_slot_time(_slot))); 52 | // } 53 | // None 54 | //} 55 | 56 | // 57 | //fn get_keys_sort_by_vote() { 58 | // 59 | //} 60 | // 61 | //// TODO 62 | //fn get_accounts(_: String) { 63 | // 64 | //} 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use std::io::{self, Write}; 69 | use super::*; 70 | 71 | #[test] 72 | fn test_get_block_slot_data() { 73 | for height in 1..12 { 74 | let slot = height - 1; 75 | let (delegate_id, slot_time) = get_block_slot_data(slot, Height(height as u64)).unwrap(); 76 | let slot_number = super::slot::get_slot_number(slot_time); 77 | writeln!(io::stdout(), "deletegate_id: {}, slot_number: {}, slot_time: {}", delegate_id, slot_number, slot_time); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/consensus/dpos/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod delegates; 2 | pub mod slot; 3 | pub mod util; -------------------------------------------------------------------------------- /src/consensus/dpos/slot.rs: -------------------------------------------------------------------------------- 1 | use time::{self, Timespec, Duration}; 2 | use chrono::*; 3 | use cryptocurrency_kit::ethkey::Address; 4 | 5 | use crate::types::Height; 6 | 7 | /// 8 | /// [1, 2, 3, 4], [5, 6, 7, 8], [9, 10] 9 | /// slot0 slot1 slot2(current slot) 10 | /// next_slot = [13, 14, 15, 16] 11 | pub const INTERVAL: i64 = 3; 12 | pub const DELEGATES: i64 = 11; 13 | pub const ACTIVE_DELEGATES:[&str; DELEGATES as usize] = [ 14 | "a", 15 | "b", 16 | "c", 17 | "d", 18 | "e", 19 | "f", 20 | "g", 21 | "h", 22 | "i", 23 | "j", 24 | "k"]; 25 | 26 | lazy_static! { 27 | static ref ACTIVE_DELEGATES_LIST: Vec<&'static str> = { 28 | let mut active_delegates = vec![]; 29 | { 30 | active_delegates.push("a"); 31 | active_delegates.push("b"); 32 | active_delegates.push("c"); 33 | active_delegates.push("d"); 34 | active_delegates.push("e"); 35 | active_delegates.push("f"); 36 | active_delegates.push("g"); 37 | active_delegates.push("h"); 38 | active_delegates.push("i"); 39 | active_delegates.push("j"); 40 | active_delegates.push("k"); 41 | } 42 | active_delegates 43 | }; 44 | } 45 | 46 | pub fn get_active_delegates<'a>(height: Height) -> Vec<&'a str> { 47 | // ACTIVE_DELEGATES.to_vec() 48 | ACTIVE_DELEGATES_LIST.clone() 49 | } 50 | 51 | /// this is a epoch time 52 | pub fn get_time(time_spec: Timespec) -> i64{ 53 | return epoch_time(time_spec) 54 | } 55 | 56 | /// real time, accurate to milliseconds 57 | pub fn get_real_time(epoch_spec: i64) -> i64 { 58 | (epoch_spec + begin_epoch_time()) * 1000 59 | } 60 | 61 | /// epoch_time time's slot 62 | pub fn get_slot_number(mut epoch_time: i64) -> i64 { 63 | if epoch_time == 0 { 64 | epoch_time = get_time(time::get_time()); 65 | } 66 | return epoch_time / INTERVAL 67 | } 68 | 69 | /// this is epoch time 70 | pub fn get_slot_time(slot: i64) -> i64{ 71 | return slot * INTERVAL 72 | } 73 | 74 | // current slot + 1 75 | pub fn get_next_slot() -> i64 { 76 | let time_now = time::get_time(); 77 | let epoch_time = get_time(time_now); 78 | let slot = get_slot_number(epoch_time); 79 | slot + 1 80 | } 81 | 82 | pub fn get_last_slot(next_slot: i64) -> i64 { 83 | next_slot + DELEGATES 84 | } 85 | 86 | // [time_spec - begin_time] 87 | fn epoch_time(time_spec: Timespec) -> i64 { 88 | time_spec.sec - begin_epoch_time() 89 | } 90 | 91 | // return begin epoch time 92 | fn begin_epoch_time() -> i64 { 93 | let epoch_time = DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap(); 94 | epoch_time.timestamp() 95 | } 96 | 97 | fn round_time(data: Timespec) -> i64 { 98 | data.sec 99 | } 100 | 101 | // calc height round 102 | fn calc_round(height: i64) -> i64{ 103 | let round = (height as f64) / (DELEGATES as f64); 104 | round.ceil() as i64 105 | } 106 | 107 | #[cfg(test)] 108 | mod tests { 109 | use std::io::{self, Write}; 110 | 111 | #[test] 112 | fn test_epoch_time(){ 113 | println!("Hello Word ...."); 114 | let epoch = super::DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap(); 115 | writeln!(io::stdout(), "{}", epoch.timestamp()).unwrap(); 116 | 117 | let time_now = super::time::get_time(); 118 | let epoch_time = super::epoch_time(time_now); 119 | writeln!(io::stdout(), "epoch time {}", epoch_time).unwrap(); 120 | } 121 | 122 | #[test] 123 | fn test_get_real_time(){ 124 | let time_now = super::time::get_time(); 125 | let epoch_time = super::get_time(time_now); 126 | assert_eq!(super::get_real_time(epoch_time), time_now.sec *1000); 127 | writeln!(io::stdout(), "real time {}", super::get_real_time(epoch_time)).unwrap(); 128 | } 129 | 130 | #[test] 131 | fn test_get_slot_number(){ 132 | let time_now = super::time::get_time(); 133 | let epoch_time = super::get_time(time_now); 134 | writeln!(io::stdout(), "epoch time {}, slot number {}",epoch_time, super::get_slot_number(epoch_time)).unwrap(); 135 | } 136 | 137 | #[test] 138 | fn test_get_next_slot_number(){ 139 | let time_now = super::time::get_time(); 140 | let epoch_time = super::get_time(time_now); 141 | let slot_number = super::get_slot_number(epoch_time); 142 | 143 | writeln!(io::stdout(), "prev slot number {}, next slot number {}", slot_number, super::get_next_slot()).unwrap(); 144 | } 145 | 146 | #[test] 147 | fn test_round_time(){ 148 | assert_eq!(super::calc_round(1), 1); 149 | assert_eq!(super::calc_round(10), 1); 150 | assert_eq!(super::calc_round(11), 1); 151 | assert_eq!(super::calc_round(12), 2); 152 | } 153 | } -------------------------------------------------------------------------------- /src/consensus/dpos/util.rs: -------------------------------------------------------------------------------- 1 | use std::string::String; 2 | 3 | // TODO: opz 4 | pub fn equal_delegate(a: &str, b: &Vec) -> bool { 5 | String::from_utf8_lossy(b).as_ref() == a 6 | } 7 | -------------------------------------------------------------------------------- /src/consensus/engine.rs: -------------------------------------------------------------------------------- 1 | use cryptocurrency_kit::ethkey::Address; 2 | use crossbeam::crossbeam_channel::Sender; 3 | 4 | use crate::types::block::{Header, Block}; 5 | 6 | //pub trait Engine { 7 | // fn author(&self, header: &Header)-> Result; 8 | // fn verify_header(&self, header: &Header, seal: bool) -> Result<(), String>; 9 | // fn verify_seal(&self, header: &Header) -> Result<(), String>; 10 | // 11 | // // preare initializes the consensus fields of a block header according to the rules of a 12 | // // particular engine. The changes are executed inline. 13 | // fn prepare(&mut self, header: &Header) -> Result<(), String>; 14 | // 15 | // fn finalize(&mut self, header: &Header) -> Result; 16 | // 17 | // // seal generate a new block for the given input block with the local miner's 18 | // // seal place on top 19 | // fn seal(&self, block: &Block, stop: Sender) -> Result; 20 | //} 21 | 22 | 23 | // Handler should be implemented is the consensus needs to handle and send peer's message 24 | pub trait Handler{ 25 | // NewChainHeader handles a new head block comes 26 | fn new_chain_head(&self) -> Result<(), String>; 27 | 28 | fn handle_message(&self, address: Address, data: &[u8]) -> Result; 29 | } -------------------------------------------------------------------------------- /src/consensus/error.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use failure::Error; 3 | 4 | use cryptocurrency_kit::crypto::Hash; 5 | 6 | use crate::types::Height; 7 | 8 | pub type ConsensusResult = Result<(), ConsensusError>; 9 | pub type EngineResult = Result<(), EngineError>; 10 | 11 | #[derive(Debug, Fail)] 12 | pub enum ConsensusError { 13 | #[fail(display = "Ignore message")] 14 | Ignored, 15 | #[fail(display = "Future message")] 16 | FutureMessage, 17 | #[fail(display = "Future round message")] 18 | FutureRoundMessage, 19 | #[fail(display = "Future unit message")] 20 | FutureBlockMessage(Height), 21 | #[fail(display = "inconsistent subjects")] 22 | InconsistentSubject, 23 | #[fail(display = "Old message")] 24 | OldMessage, 25 | #[fail(display = "Invalid message")] 26 | InvalidMessage, 27 | #[fail(display = "Unauthorized address")] 28 | UnauthorizedAddress, 29 | #[fail(display = "Waiting for new round")] 30 | WaitNewRound, 31 | #[fail(display = "Not from proposer")] 32 | NotFromProposer, 33 | #[fail(display = "Timeout message")] 34 | TimeoutMessage, 35 | #[fail(display = "An unknown error has occurred, ({})", _0)] 36 | Unknown(String), 37 | #[fail(display = "engine error hash occurred, ({})", _0)] 38 | Engine(EngineError), 39 | } 40 | 41 | #[derive(Debug, Fail)] 42 | pub enum EngineError { 43 | #[fail(display = "engine is not started")] 44 | EngineNotStarted, 45 | #[fail(display = "Invalid proposal")] 46 | InvalidProposal, 47 | #[fail(display = "Invalid signature")] 48 | InvalidSignature, 49 | #[fail(display = "Invalid height")] 50 | InvalidHeight, 51 | #[fail(display = "Invalid timestamp")] 52 | InvalidTimestamp, 53 | #[fail(display = "Invalid transaction hash, expect: {:?}, got: {:?}", _0, _1)] 54 | InvalidTransactionHash(Hash, Hash), 55 | #[fail(display = "Unauthorized")] 56 | Unauthorized, 57 | #[fail(display = "Lack votes, expect: {}, got: {}", _0, _1)] 58 | LackVotes(usize, usize), 59 | #[fail(display = "Block in the future")] 60 | FutureBlock, 61 | #[fail(display = "Invalid block number")] 62 | InvalidBlock, 63 | #[fail(display = "Unknown ancestor, child:{:?}, parent: {:?}", _0, _1)] 64 | UnknownAncestor(Height, Height), 65 | #[fail(display = "Consensus interrupt")] 66 | Interrupt, 67 | #[fail(display = "An unknown error has occurred, ({})", _0)] 68 | Unknown(String), 69 | } 70 | -------------------------------------------------------------------------------- /src/consensus/events.rs: -------------------------------------------------------------------------------- 1 | use std::any::{Any, TypeId}; 2 | 3 | use ::actix::prelude::*; 4 | 5 | use crate::{ 6 | protocol::GossipMessage, 7 | types::Height, 8 | }; 9 | use super::{ 10 | types::{Proposal, View}, 11 | error::ConsensusResult, 12 | }; 13 | 14 | #[derive(Debug, Message)] 15 | pub enum OpCMD { 16 | stop, 17 | Ping, 18 | } 19 | 20 | #[derive(Debug)] 21 | pub enum RequestEventType { 22 | Block, 23 | Msg, 24 | } 25 | 26 | pub struct RequestEvent { 27 | proposal: Proposal, 28 | } 29 | 30 | fn is_view(_s: &T) -> bool { 31 | TypeId::of::() == TypeId::of::() 32 | } 33 | 34 | #[derive(Debug)] 35 | pub struct NewHeaderEvent { 36 | pub proposal: Proposal, 37 | } 38 | 39 | impl Message for NewHeaderEvent { 40 | type Result = ConsensusResult; 41 | } 42 | 43 | #[derive(Debug)] 44 | pub struct MessageEvent { 45 | pub payload: Vec, 46 | } 47 | 48 | impl Message for MessageEvent { 49 | type Result = ConsensusResult; 50 | } 51 | 52 | #[derive(Debug, Message)] 53 | pub struct FinalCommittedEvent {} 54 | 55 | #[derive(Debug, Message)] 56 | pub struct TimerEvent {} 57 | 58 | #[derive(Debug)] 59 | pub struct BackLogEvent { 60 | pub msg: GossipMessage, 61 | } 62 | 63 | impl Message for BackLogEvent { 64 | type Result = ConsensusResult; 65 | } 66 | 67 | #[derive(Debug, Message)] 68 | pub enum ConsensusEvent { 69 | NetWork(MessageEvent), 70 | FinalCommitted(FinalCommittedEvent), 71 | Timer(TimerEvent), 72 | BackLog(BackLogEvent), 73 | } 74 | 75 | #[cfg(test)] 76 | mod test { 77 | use super::*; 78 | 79 | #[derive(Debug)] 80 | struct testView {} 81 | 82 | impl ::std::fmt::Display for testView { 83 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 84 | write!(f, "") 85 | } 86 | } 87 | 88 | #[test] 89 | fn test_type_of() { 90 | let view = View { height: 10, round: 20 }; 91 | assert!(is_view(&view)); 92 | assert!(!is_view(&testView {})); 93 | } 94 | } -------------------------------------------------------------------------------- /src/consensus/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod consensus; 2 | pub mod config; 3 | pub mod validator; 4 | pub mod types; 5 | pub mod events; 6 | pub mod backend; 7 | pub mod engine; 8 | pub mod error; 9 | pub mod pbft; 10 | -------------------------------------------------------------------------------- /src/consensus/pbft/core/back_log.rs: -------------------------------------------------------------------------------- 1 | use actix::{Actor, Addr, Arbiter, Context, Handler, msgs, System}; 2 | use actix::AsyncContext; 3 | use priority_queue::PriorityQueue; 4 | use cryptocurrency_kit::ethkey::Address; 5 | use cryptocurrency_kit::crypto::{hash, CryptoHash, Hash}; 6 | use cryptocurrency_kit::storage::values::StorageValue; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use std::borrow::Cow; 10 | use std::io::Cursor; 11 | use std::collections::HashMap; 12 | use std::time::Duration; 13 | 14 | use crate::protocol::{GossipMessage, MessageType, to_priority}; 15 | use crate::consensus::types::{View, Subject, PrePrepare}; 16 | use crate::consensus::validator::ImplValidatorSet; 17 | use super::core::Core; 18 | 19 | pub struct BackLogActor { 20 | qp: HashMap>, 21 | core: Addr, 22 | } 23 | 24 | 25 | impl Actor for BackLogActor { 26 | type Context = Context; 27 | 28 | fn started(&mut self, ctx: &mut Self::Context) { 29 | info!("Back Log actor has started"); 30 | self.process_back_log(ctx); 31 | } 32 | 33 | fn stopped(&mut self, _ctx: &mut Self::Context) { 34 | info!("Back Log actor has stoppped"); 35 | } 36 | } 37 | 38 | impl Handler for BackLogActor { 39 | type Result = (); 40 | fn handle(&mut self, msg: GossipMessage, _ctx: &mut Context) -> Self::Result { 41 | match &msg.code { 42 | MessageType::Preprepare => { 43 | let msg_payload = msg.msg(); 44 | let preprepare: PrePrepare = PrePrepare::from_bytes(Cow::from(msg_payload)); 45 | let view = preprepare.view; 46 | let weight = to_priority(MessageType::Preprepare, view); 47 | self.qp.entry(msg.address).or_insert_with(|| { 48 | let mut qp = PriorityQueue::new(); 49 | qp.push(msg, weight); 50 | qp 51 | }); 52 | } 53 | other_code => { 54 | let msg_payload = msg.msg(); 55 | let subject: Subject = Subject::from_bytes(Cow::from(msg_payload)); 56 | let weight = to_priority(other_code.clone(), subject.view); 57 | self.qp.entry(msg.address).or_insert_with(|| { 58 | let mut qp = PriorityQueue::new(); 59 | qp.push(msg, weight); 60 | qp 61 | }); 62 | } 63 | } 64 | () 65 | } 66 | } 67 | 68 | impl BackLogActor { 69 | pub fn new(core_pid: Addr) -> Self { 70 | BackLogActor { core: core_pid, qp: HashMap::new() } 71 | } 72 | 73 | fn process_back_log(&self, ctx: &mut actix::Context) { 74 | ctx.run_interval(Duration::from_millis(100), |act, _ctx| { 75 | for (_key, value) in act.qp.iter_mut() { 76 | for (message, _) in value.iter_mut() { 77 | let view; 78 | match &message.code { 79 | MessageType::Preprepare => { 80 | let preprepare: PrePrepare = PrePrepare::from_bytes(Cow::from(message.msg())); 81 | view = preprepare.view; 82 | } 83 | _other_type => { 84 | let subject: Subject = Subject::from_bytes(Cow::from(message.msg())); 85 | view = subject.view; 86 | } 87 | } 88 | } 89 | } 90 | }); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/consensus/pbft/core/commit.rs: -------------------------------------------------------------------------------- 1 | use cryptocurrency_kit::crypto::Hash; 2 | use cryptocurrency_kit::ethkey::{ 3 | public_to_address, recover, verify_address, Address, Message as SignMessage, Signature, 4 | }; 5 | use cryptocurrency_kit::storage::values::StorageValue; 6 | 7 | use super::core::Core; 8 | use crate::{ 9 | consensus::error::{ConsensusError, ConsensusResult}, 10 | consensus::types::{Subject, View}, 11 | consensus::validator::ValidatorSet, 12 | protocol::{GossipMessage, MessageType, State}, 13 | types::{ 14 | votes::{decrypt_commit_bytes, encrypt_commit_bytes, Votes}, 15 | Validator, 16 | }, 17 | }; 18 | 19 | use std::borrow::Cow; 20 | use ethereum_types::H256; 21 | use cryptocurrency_kit::common::to_fixed_array_32; 22 | 23 | pub trait HandleCommit { 24 | fn send_commit(&mut self); 25 | fn send_commit_for_old_block(&mut self, view: &View, digest: Hash); 26 | fn broadcast_commit(&mut self, sub: &Subject, seal: Hash); 27 | fn handle(&mut self, msg: &GossipMessage, src: &Validator) -> Result<(), ConsensusError>; 28 | fn verify_commit( 29 | &self, 30 | commit_seal: Option<&Signature>, 31 | subject: &Subject, 32 | sender: Address, 33 | src: Validator, 34 | ) -> Result<(), ConsensusError>; 35 | fn accept(&mut self, msg: &GossipMessage, src: &Validator) -> Result<(), ConsensusError>; 36 | } 37 | 38 | impl HandleCommit for Core { 39 | fn send_commit(&mut self) { 40 | let current_state = &self.current_state; 41 | let proposal = current_state.proposal().unwrap(); 42 | let block = proposal.block(); 43 | let subject = current_state.subject(); 44 | self.broadcast_commit(subject.as_ref().unwrap(), block.hash()) 45 | } 46 | 47 | fn send_commit_for_old_block(&mut self, view: &View, digest: Hash) { 48 | let subject = Subject { 49 | view: view.clone(), 50 | digest: digest, 51 | }; 52 | self.broadcast_commit(&subject, digest) 53 | } 54 | 55 | // TOOD 56 | fn broadcast_commit(&mut self, subject: &Subject, _digest: Hash) { 57 | trace!("broadcast commit"); 58 | let commit_seal = encrypt_commit_bytes(&subject.digest, self.keypair.secret()); 59 | let encoded_subject = subject.clone().into_bytes(); 60 | let msg = GossipMessage::new(MessageType::Commit, encoded_subject, Some(commit_seal)); 61 | self.broadcast(&msg); 62 | } 63 | 64 | // handle commit type message 65 | fn handle(&mut self, msg: &GossipMessage, src: &Validator) -> Result<(), ConsensusError> { 66 | debug!("Handle commit message from {:?}", src.address()); 67 | let subject = Subject::from(msg.msg()); 68 | // let _current_subject = self.current_state.subject().unwrap(); 69 | self.check_message(MessageType::Commit, &subject.view)?; 70 | let sender = msg.address; 71 | let subject = Subject::from_bytes(Cow::from(msg.msg())); 72 | self.verify_commit(msg.commit_seal.as_ref(), &subject, sender, src.clone())?; 73 | debug!( 74 | "Pass very commit, commit size:{}, state:{:?}, {}", 75 | self.current_state.commits.len(), 76 | self.state, 77 | msg.trace() 78 | ); 79 | ::accept(self, msg, src)?; 80 | let val_set = self.val_set(); 81 | // receive more +2/3 votes 82 | if self.current_state.commits.len() > val_set.two_thirds_majority() 83 | && self.state < State::Committed 84 | { 85 | self.current_state.lock_hash(); 86 | self.commit(); 87 | } 88 | Ok(()) 89 | } 90 | 91 | fn verify_commit( 92 | &self, 93 | commit_seal: Option<&Signature>, 94 | commit_subject: &Subject, 95 | sender: Address, 96 | _src: Validator, 97 | ) -> Result<(), ConsensusError> { 98 | if commit_seal.is_none() { 99 | return Err(ConsensusError::Unknown("commit seal is nil".to_string())); 100 | } 101 | let commit_seal = commit_seal.unwrap(); 102 | let digest = H256::from(to_fixed_array_32(commit_subject.digest.as_ref())); 103 | let sign_message = SignMessage::from(digest); 104 | verify_address(&sender, commit_seal, &sign_message) 105 | .map(|_| ()) 106 | .map_err(|_| { 107 | ConsensusError::Unknown("message's sender should be commit seal".to_string()) 108 | })?; 109 | let current_state = &self.current_state; 110 | let current_subject = current_state.subject().unwrap(); 111 | if current_subject.digest != commit_subject.digest 112 | || current_subject.view != commit_subject.view 113 | { 114 | warn!( 115 | "Inconsistent subjects between commit and proposal, d1={}, d2={}", 116 | current_subject.digest.short(), 117 | commit_subject.digest.short() 118 | ); 119 | //return Err(ConsensusError::Unknown( 120 | // "Inconsistent subjects between commit and proposal".to_string(), 121 | //)); 122 | } 123 | Ok(()) 124 | } 125 | 126 | fn accept(&mut self, msg: &GossipMessage, _: &Validator) -> ConsensusResult { 127 | self.current_state 128 | .commits 129 | .add(msg.clone()) 130 | .map_err(|err| ConsensusError::Unknown(err)) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/consensus/pbft/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod core; 2 | pub mod round_state; 3 | pub mod back_log; 4 | mod timer; 5 | pub mod types; 6 | mod round_change_set; 7 | pub mod new_header; 8 | pub mod request; 9 | pub mod preprepare; 10 | pub mod prepare; 11 | pub mod commit; 12 | pub mod round_change; -------------------------------------------------------------------------------- /src/consensus/pbft/core/new_header.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | consensus::error::{ConsensusError, ConsensusResult}, 3 | consensus::types::{Subject, View, Request}, 4 | consensus::validator::ValidatorSet, 5 | protocol::{GossipMessage, MessageType, State}, 6 | consensus::events::{RequestEvent, NewHeaderEvent}, 7 | types::Validator, 8 | }; 9 | 10 | use super::{ 11 | core::Core, 12 | commit::HandleCommit, 13 | request::HandlerRequest, 14 | }; 15 | 16 | pub trait HandleNewHeader { 17 | fn handle(&mut self, msg: &NewHeaderEvent, src: &Validator) -> ConsensusResult; 18 | } 19 | 20 | impl HandleNewHeader for Core { 21 | fn handle(&mut self, msg: &NewHeaderEvent, src: &Validator) -> ConsensusResult { 22 | // start new round, height = last_height + 1 23 | self.start_new_zero_round(); 24 | ::handle(self, &Request::new(msg.proposal.clone())) 25 | } 26 | } -------------------------------------------------------------------------------- /src/consensus/pbft/core/prepare.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::time::Duration; 3 | 4 | use cryptocurrency_kit::storage::values::StorageValue; 5 | 6 | 7 | use crate::{ 8 | consensus::error::{ConsensusError, ConsensusResult}, 9 | consensus::types::{Subject, View}, 10 | consensus::validator::ValidatorSet, 11 | protocol::{GossipMessage, MessageType, State}, 12 | types::Validator, 13 | }; 14 | 15 | use super::{ 16 | core::Core, 17 | commit::HandleCommit, 18 | }; 19 | 20 | pub trait HandlePrepare { 21 | fn send_prepare(&mut self); 22 | fn verify_prepare(&mut self, prepare: &Subject, src: &Validator) -> ConsensusResult; 23 | fn handle(&mut self, msg: &GossipMessage, src: &Validator) -> ConsensusResult; 24 | fn accept(&mut self, msg: &GossipMessage, src: &Validator) -> ConsensusResult; 25 | } 26 | 27 | impl HandlePrepare for Core { 28 | fn send_prepare(&mut self) { 29 | let current_view = self.current_view(); 30 | let subject = self.current_state.subject().as_ref().cloned().unwrap(); 31 | let payload = subject.into_bytes(); 32 | 33 | self.broadcast(&GossipMessage::new( 34 | MessageType::Prepare, 35 | payload, 36 | None, 37 | )); 38 | } 39 | 40 | fn verify_prepare(&mut self, subject: &Subject, _src: &Validator) -> ConsensusResult { 41 | let current_view = self.current_view(); 42 | if current_view != subject.view { 43 | return Err(ConsensusError::InconsistentSubject); 44 | } 45 | Ok(()) 46 | } 47 | 48 | fn handle(&mut self, msg: &GossipMessage, src: &Validator) -> ConsensusResult { 49 | let subject: Subject = Subject::from_bytes(Cow::from(msg.msg())); 50 | self.check_message(MessageType::Prepare, &subject.view)?; 51 | self.verify_prepare(&subject, src)?; 52 | ::accept(self, msg, src)?; 53 | // Add lock hash prove 54 | if self.current_state.is_locked() && subject.digest == *self.current_state.get_lock_hash().as_ref().unwrap() { 55 | self.current_state.lock_hash(); 56 | self.set_state(State::Prepared); 57 | self.send_commit(); 58 | } 59 | if self.current_state.get_prepare_or_commit_size() > self.val_set().two_thirds_majority() { 60 | self.current_state.lock_hash(); 61 | self.set_state(State::Prepared); 62 | self.send_commit(); 63 | } 64 | 65 | Ok(()) 66 | } 67 | 68 | fn accept(&mut self, msg: &GossipMessage, _src: &Validator) -> ConsensusResult { 69 | self.current_state 70 | .prepares 71 | .add(msg.clone()) 72 | .map_err(|err| ConsensusError::Unknown(err)) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/consensus/pbft/core/preprepare.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::time::Duration; 3 | 4 | use cryptocurrency_kit::storage::values::StorageValue; 5 | use cryptocurrency_kit::crypto::{CryptoHash, Hash, hash}; 6 | 7 | use crate::{ 8 | consensus::error::{ConsensusError, ConsensusResult, EngineError}, 9 | consensus::types::{PrePrepare, Proposal, Request, Subject}, 10 | consensus::validator::Validators, 11 | consensus::validator::ValidatorSet, 12 | protocol::{GossipMessage, MessageType, State}, 13 | types::Validator, 14 | }; 15 | 16 | use super::{ 17 | round_change::HandleRoundChange, 18 | core::Core, 19 | commit::HandleCommit, 20 | prepare::HandlePrepare, 21 | }; 22 | 23 | pub trait HandlePreprepare { 24 | fn send_preprepare(&mut self, requst: &Request); 25 | fn handle(&mut self, msg: &GossipMessage, src: &Validator) -> Result<(), ConsensusError>; 26 | fn accetp(&mut self, preprepare: &PrePrepare); 27 | } 28 | 29 | impl HandlePreprepare for Core { 30 | fn send_preprepare(&mut self, request: &Request) { 31 | //TODO add lock hash prove 32 | if self.current_state.height() == request.proposal().block().height() && self.is_proposer() 33 | { 34 | let preprepre = PrePrepare::new(self.current_view(), request.proposal.clone()); 35 | self.broadcast(&GossipMessage::new( 36 | MessageType::Preprepare, 37 | preprepre.into_bytes(), 38 | None, 39 | )); 40 | } else { 41 | debug!("Im's not proposer"); 42 | } 43 | } 44 | 45 | fn handle(&mut self, msg: &GossipMessage, src: &Validator) -> ConsensusResult { 46 | let preprepare: PrePrepare = PrePrepare::from_bytes(Cow::from(msg.msg())); 47 | let result = self.check_message(MessageType::Preprepare, &preprepare.view); 48 | // Ensure we have the same view with the PRE-PREPARE message 49 | // If it is old message, see if we need to broadcast COMMIT 50 | if let Err(ref err) = result { 51 | match err { 52 | ConsensusError::OldMessage => { 53 | let block = preprepare.proposal.block(); 54 | let pre_header = match self.backend.get_header_by_height(block.height()) { 55 | Some(header) => { 56 | header 57 | } 58 | None => { 59 | return Err(ConsensusError::Engine(EngineError::InvalidProposal)); 60 | } 61 | }; 62 | if pre_header.block_hash() != block.hash() { 63 | return Err(ConsensusError::Engine(EngineError::InvalidProposal)); 64 | } 65 | let pre_height = block.height() - 1; 66 | let mut val_set = self.backend.validators(pre_height).clone(); 67 | let _previous_proposer = self.backend.get_proposer(pre_height); 68 | val_set.calc_proposer(&block.header().prev_hash, pre_height, preprepare.view.round); 69 | if val_set.is_proposer(src.address().clone()) 70 | && self.backend.has_proposal(&block.hash(), block.height()) 71 | { 72 | self.send_commit_for_old_block(&preprepare.view, block.hash()); 73 | } 74 | } 75 | ConsensusError::FutureBlockMessage(_) => { 76 | // forward EngineError::FutureBlock to handle 77 | // self.new_round_future_preprepare_timer() 78 | } 79 | _ => return result 80 | } 81 | } 82 | 83 | let val_set = self.val_set(); 84 | if val_set.is_proposer(src.address().clone()) == false { 85 | return Err(ConsensusError::NotFromProposer); 86 | } 87 | 88 | // TODO 89 | let (d, result) = self 90 | .backend 91 | .verify(&preprepare.proposal); 92 | 93 | if let Err(ref err) = result { 94 | match err { 95 | EngineError::FutureBlock => { 96 | self.new_round_future_preprepare_timer(d, msg.clone()); 97 | return Err(ConsensusError::FutureBlockMessage(preprepare.proposal.block().height())); 98 | } 99 | // other error 100 | _ => { 101 | // send next round change, because proposal is invalid, so proposer is bad node 102 | self.send_next_round_change(); 103 | return Err(ConsensusError::Unknown(format!("{}", err))); 104 | } 105 | } 106 | } 107 | 108 | if self.state == State::AcceptRequest { 109 | if self.current_state.is_locked() { 110 | if preprepare.proposal.block().hash() == self.current_state.get_lock_hash().unwrap() { 111 | ::accetp(self, &preprepare); 112 | self.set_state(State::Prepared); 113 | self.send_commit(); 114 | } else { 115 | self.send_next_round_change(); 116 | } 117 | } else { 118 | ::accetp(self, &preprepare); 119 | self.set_state(State::PrePrepared); 120 | self.send_prepare(); 121 | } 122 | } 123 | 124 | // TODO 125 | Ok(()) 126 | } 127 | 128 | fn accetp(&mut self, preprepare: &PrePrepare) { 129 | let header = preprepare.proposal.block().header(); 130 | self.consensus_timestamp = Duration::from_nanos(header.time); 131 | self.current_state.set_preprepare(preprepare.clone()) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/consensus/pbft/core/request.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use crate::{ 4 | protocol::State, 5 | consensus::error::{ConsensusError, ConsensusResult}, 6 | consensus::types::{Request, PrePrepare, Proposal}, 7 | }; 8 | 9 | use super::core::Core; 10 | use super::preprepare::HandlePreprepare; 11 | 12 | pub trait HandlerRequest { 13 | fn handle(&mut self, request: &Request) -> ConsensusResult; 14 | fn check_request_message(&self, request: &Request) ->ConsensusResult; 15 | fn accept(&mut self, request: &Request); 16 | } 17 | 18 | impl HandlerRequest for Core { 19 | fn handle(&mut self, request: &Request) -> ConsensusResult { 20 | self.check_request_message(request)?; 21 | assert_eq!(self.state, State::AcceptRequest); 22 | ::accept(self, request); 23 | self.send_preprepare(request); 24 | Ok(()) 25 | } 26 | 27 | fn check_request_message(&self, request: &Request) -> ConsensusResult { 28 | if self.current_state.height() == 0 { 29 | return Err(ConsensusError::WaitNewRound); 30 | } 31 | if self.current_state.height() > request.proposal.block().height() { 32 | return Err(ConsensusError::OldMessage); 33 | } 34 | if self.current_state.height() < request.proposal.block().height() { 35 | return Err(ConsensusError::FutureMessage); 36 | } 37 | Ok(()) 38 | } 39 | 40 | fn accept(&mut self, request: &Request) { 41 | self.current_state.pending_request = Some(Request{proposal: request.proposal.clone()}); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/consensus/pbft/core/round_change.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::time::Instant; 3 | use std::time::Duration; 4 | 5 | use cryptocurrency_kit::crypto::EMPTY_HASH; 6 | use cryptocurrency_kit::storage::values::StorageValue; 7 | 8 | use crate::{ 9 | consensus::error::{ConsensusError, ConsensusResult}, 10 | consensus::validator::ValidatorSet, 11 | consensus::types::{Round, Subject, View}, 12 | protocol::{GossipMessage, MessageType}, 13 | types::Validator, 14 | }; 15 | 16 | use super::core::Core; 17 | 18 | pub trait HandleRoundChange { 19 | fn send_next_round_change(&mut self); 20 | fn send_round_change(&mut self, round: Round); 21 | // receive a round change message and handle it 22 | fn handle(&mut self, msg: &GossipMessage, src: &Validator) -> ConsensusResult; 23 | } 24 | 25 | impl HandleRoundChange for Core { 26 | fn send_next_round_change(&mut self) { 27 | let current_view = self.current_view(); 28 | self.round_change_set.print_info(); 29 | // Find the max 30 | let round = self.round_change_set.max_round(); 31 | if round <= current_view.round { 32 | self.send_round_change(current_view.round + 1); 33 | } else { 34 | self.send_round_change(round); 35 | } 36 | } 37 | 38 | fn send_round_change(&mut self, round: Round) { 39 | if Instant::now().duration_since(self.round_change_limiter) <= Duration::from_millis(50) { 40 | debug!("Skip round change sent"); 41 | self.new_round_change_timer(); 42 | return; 43 | } 44 | self.round_change_limiter = Instant::now(); 45 | 46 | if self.current_view().round < round { 47 | self.catchup_round(round); 48 | } 49 | let current_view = self.current_view(); 50 | 51 | // let ok = current_view.round < round; 52 | // assert!(ok); 53 | 54 | // TODO add pre max round change prove 55 | let subject = Subject { 56 | view: View::new(current_view.height, round), 57 | digest: EMPTY_HASH, 58 | }; 59 | debug!("Vote for round change, current:{}, vote: {}", current_view.round, round); 60 | let mut msg = GossipMessage::new(MessageType::RoundChange, subject.into_bytes(), None); 61 | msg.create_time = chrono::Local::now().timestamp_millis() as u64; 62 | self.broadcast(&msg); 63 | } 64 | 65 | fn handle(&mut self, msg: &GossipMessage, src: &Validator) -> ConsensusResult { 66 | let subject: Subject = Subject::from_bytes(Cow::from(msg.msg())); 67 | debug!("Handle round change message from {:?}, from me: {}, subject: {:?}", src.address(), self.address() == *src.address(), subject); 68 | self.check_message(MessageType::RoundChange, &subject.view)?; 69 | let current_view = self.current_view(); 70 | let current_val_set = self.val_set().clone(); 71 | if current_view.round > subject.view.round && subject.view.round > 0 { 72 | debug!("round change, current_round:{}, round:{}", current_view.round, subject.view.round, ); 73 | // may be peer is less than network node 74 | self.send_round_change(subject.view.round); 75 | return Ok(()); 76 | } 77 | 78 | let n = self 79 | .round_change_set 80 | .add(subject.view.round, msg.clone()) 81 | .map_err(|err| ConsensusError::Unknown(err))?; 82 | debug!("round change, current_round:{}, round:{}, votes size {}", current_view.round, subject.view.round, n); 83 | 84 | // check round change more detail 85 | // if n >= (current_val_set.two_thirds_majority() + 1) 86 | // && (self.wait_round_change && current_view.round < subject.view.round) { 87 | if n >= (current_val_set.two_thirds_majority() + 1) 88 | && (current_view.round < subject.view.round) { 89 | // 注意:假设节点刚起动,这时候,其wait_round_change 可能未false,这样即使收到了超过+2/3的票,如果采用 90 | // n == (current_val_set.two_thirds_majority() + 1, 是有问题的 91 | // receive more than local round and +2/3 has vote it 92 | self.send_round_change(subject.view.round); 93 | self.start_new_round(subject.view.round, &vec![]); 94 | return Ok(()); 95 | } else if self.wait_round_change && current_view.round < subject.view.round { 96 | // receive more than local round 97 | return Err(ConsensusError::FutureRoundMessage); 98 | } 99 | Ok(()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/consensus/pbft/core/round_change_set.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ 4 | protocol::{MessageManage, GossipMessage}, 5 | consensus::types::{Round, View}, 6 | consensus::validator::{ValidatorSet, Validators, ImplValidatorSet}, 7 | }; 8 | 9 | type PreRCSignBytes = Vec>; 10 | 11 | pub struct RoundChangeSet { 12 | validator_set: V, 13 | // 当前所有轮次的validators 14 | round_changes: HashMap, 15 | // 每个轮次的消息管理器 16 | pre_rc_sign_bytes: Option, // 暂时未用 17 | } 18 | 19 | impl RoundChangeSet { 20 | pub fn new(validators: ImplValidatorSet, pre_rc_sign_bytes: Option) -> RoundChangeSet { 21 | RoundChangeSet { 22 | validator_set: validators, 23 | round_changes: HashMap::new(), 24 | pre_rc_sign_bytes: pre_rc_sign_bytes, 25 | } 26 | } 27 | 28 | pub fn add(&mut self, round: Round, msg: GossipMessage) -> Result { 29 | let val_set = self.validator_set.clone(); 30 | let msg_manager = self.round_changes.entry(round).or_insert_with(|| { 31 | MessageManage::new(View::default(), val_set) 32 | }); 33 | msg_manager.add(msg).map(|_| { 0 })?; 34 | Ok(msg_manager.len()) 35 | } 36 | 37 | pub fn round_change_set(&self, round: &Round) -> Option<&MessageManage> { 38 | self.round_changes.get(round) 39 | } 40 | 41 | // TODO 42 | // pub fn pre_round_change_bytes(&self) 43 | 44 | // pub fn max_round_change_changes_bytes(&self, n: usize) -> (Round, &Vec>) { 45 | // 46 | // } 47 | pub fn clear(&mut self, round: Round) { 48 | // dereference 49 | self.round_changes.retain(|&round_, mm| { 50 | mm.len() > 0 && round > round_ 51 | }); 52 | } 53 | 54 | // return the max round which the number of messages is equal or larger than num 55 | pub fn max_round_more_than_n(&self, num: usize) -> Option { 56 | if let Some((round, _)) = self.round_changes.iter().max_by(|x, y| x.0.cmp(y.0)) { 57 | if self.round_changes.get(round).unwrap().len() >= num { 58 | return Some(*round); 59 | } 60 | } 61 | None 62 | } 63 | 64 | pub fn max_round(&self) -> Round { 65 | let mut max = 0; 66 | let mut total = 0; 67 | self.round_changes.iter().for_each(|x| { 68 | if x.1.len() >= total && *x.0 > max { 69 | max = *x.0; 70 | total = x.1.len(); 71 | }; 72 | }); 73 | max 74 | } 75 | 76 | pub fn print_info(&self) { 77 | for round_change in &self.round_changes { 78 | debug!("round:{:?}, size:{:?}", round_change.0, round_change.1.len()); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/consensus/pbft/core/round_state.rs: -------------------------------------------------------------------------------- 1 | use cryptocurrency_kit::crypto::{Hash, EMPTY_HASH}; 2 | use cryptocurrency_kit::storage::values::StorageValue; 3 | 4 | use crate::{ 5 | consensus::validator::{ValidatorSet, ImplValidatorSet}, 6 | consensus::types::{PrePrepare, Proposal, Request, Round, Subject, View}, 7 | protocol::{GossipMessage, MessageManage, MessageType}, 8 | types::Height, 9 | }; 10 | 11 | // it is not safe 12 | pub struct RoundState { 13 | round: Round, 14 | height: Height, 15 | // 提案 16 | pub preprepare: Option, 17 | pub prepares: MessageManage, 18 | pub commits: MessageManage, 19 | pub pending_request: Option>, 20 | // 自己的提案 21 | lock_hash: Option, // 锁hash 22 | } 23 | 24 | 25 | impl RoundState 26 | { 27 | pub(crate) fn new_round_state(view: View, vals: ImplValidatorSet, 28 | lock_hash: Option, 29 | preprepare: Option, 30 | pending_request: Option>) 31 | -> Self { 32 | RoundState { 33 | round: view.round, 34 | height: view.height, 35 | preprepare: preprepare, 36 | prepares: MessageManage::new(view.clone(), vals.clone()), 37 | commits: MessageManage::new(view.clone(), vals.clone()), 38 | pending_request: pending_request, 39 | lock_hash: lock_hash, 40 | } 41 | } 42 | 43 | pub(crate) fn get_prepare_or_commit_size(&self) -> usize { 44 | let mut result = self.prepares.len() + self.commits.len(); 45 | self.prepares.values().iter().for_each(|message| { 46 | if self.commits.get_message(message.address).is_some() { 47 | result -= 1; 48 | } 49 | }); 50 | 51 | result 52 | } 53 | 54 | pub(crate) fn subject(&self) -> Option { 55 | if self.preprepare.is_none() { 56 | return None; 57 | } 58 | Some(Subject { 59 | view: View { 60 | round: self.round, 61 | height: self.height, 62 | }, 63 | digest: self.preprepare.as_ref().unwrap().proposal.block().hash(), 64 | }) 65 | } 66 | 67 | pub(crate) fn set_preprepare(&mut self, preprepare: PrePrepare) { 68 | self.preprepare = Some(preprepare); 69 | } 70 | 71 | pub(crate) fn proposal(&self) -> Option<&Proposal> { 72 | if self.preprepare.is_none() { 73 | None 74 | } else { 75 | Some(&self.preprepare.as_ref().unwrap().proposal) 76 | } 77 | } 78 | 79 | pub(crate) fn set_round(&mut self, round: Round) { 80 | self.round = round; 81 | } 82 | 83 | pub(crate) fn round(&self) -> Round { 84 | self.round 85 | } 86 | 87 | pub(crate) fn set_height(&mut self, height: Height) { 88 | self.height = height; 89 | } 90 | 91 | pub(crate) fn height(&self) -> Height { 92 | self.height 93 | } 94 | 95 | pub(crate) fn is_locked(&self) -> bool { 96 | self.lock_hash.is_some() 97 | } 98 | 99 | // 锁定提案 100 | pub(crate) fn lock_hash(&mut self) { 101 | if self.preprepare.is_none() { 102 | return; 103 | } 104 | 105 | self.lock_hash = Some(self.preprepare.as_ref().unwrap().proposal.block().hash()); 106 | trace!( 107 | "Lock proposal, hash:{}", 108 | self.lock_hash.as_ref().unwrap().short() 109 | ); 110 | } 111 | 112 | // 解锁提案 113 | pub(crate) fn unlock_hash(&mut self) { 114 | trace!( 115 | "Unlock proposal, hash:{}", 116 | self.lock_hash.as_ref().or_else(|| Some(&EMPTY_HASH)).unwrap().short() 117 | ); 118 | self.lock_hash = None; 119 | } 120 | 121 | pub(crate) fn get_lock_hash(&self) -> Option { 122 | self.lock_hash.as_ref().cloned() 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/consensus/pbft/core/timer.rs: -------------------------------------------------------------------------------- 1 | use ::actix::prelude::*; 2 | use uuid::Uuid; 3 | 4 | use std::time::Duration; 5 | 6 | use crate::{ 7 | consensus::validator::{ValidatorSet, ImplValidatorSet}, 8 | consensus::events::{TimerEvent, BackLogEvent}, 9 | protocol::GossipMessage, 10 | common::random_uuid, 11 | }; 12 | 13 | use super::core::Core; 14 | 15 | #[derive(Debug, Message)] 16 | pub enum Op { 17 | Stop, 18 | Interval, 19 | } 20 | 21 | pub struct Timer { 22 | uuid: Uuid, 23 | name: String, 24 | pub interval: Duration, 25 | pub pid: Option>, 26 | msg: Option, 27 | } 28 | 29 | impl Actor for Timer { 30 | type Context = Context; 31 | 32 | fn started(&mut self, ctx: &mut Self::Context) { 33 | trace!("[{:?}]{}'s timer actor has started, du:{:?}", self.uuid.to_string(), self.name, self.interval.as_secs()); 34 | ctx.notify_later(Op::Interval, self.interval); 35 | } 36 | 37 | fn stopped(&mut self, _: &mut Self::Context) { 38 | trace!("[{:?}]{}'s timer actor has stopped", self.uuid.to_string(), self.name); 39 | } 40 | } 41 | 42 | impl Handler for Timer { 43 | type Result = (); 44 | fn handle(&mut self, msg: Op, ctx: &mut Self::Context) -> Self::Result { 45 | // info!("[{:?}]{}'s timer actor triggers, op:{:?}", self.uuid.to_string(), self.name, msg); 46 | match msg { 47 | Op::Stop => ctx.stop(), 48 | Op::Interval => { 49 | if self.pid.is_some() { 50 | if let Some(ref msg) = self.msg { 51 | self.pid.as_ref().unwrap().do_send(BackLogEvent { msg: msg.clone() }) 52 | } else { 53 | //FIXME 54 | if self.name == "future" { 55 | return; 56 | } 57 | self.pid.as_ref().unwrap().do_send(TimerEvent {}) 58 | }; 59 | } 60 | } 61 | } 62 | () 63 | } 64 | } 65 | 66 | impl Timer { 67 | pub fn new(name: String, interval: Duration, pid: Addr, msg: Option) -> Self { 68 | Timer { uuid: random_uuid(), name, interval, pid: Some(pid), msg: msg } 69 | } 70 | 71 | pub fn new_tmp(name: String, interval: Duration) -> Self { 72 | Timer { uuid: random_uuid(), name, interval, pid: None, msg: None } 73 | } 74 | } -------------------------------------------------------------------------------- /src/consensus/pbft/core/types.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laohanlinux/consensus-rs/ebf0879ea3cf653c62ac79ff658c804a3bddcc6f/src/consensus/pbft/core/types.rs -------------------------------------------------------------------------------- /src/consensus/pbft/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod core; -------------------------------------------------------------------------------- /src/core/actor.rs: -------------------------------------------------------------------------------- 1 | use ::actix::prelude::*; 2 | use actix_broker::BrokerSubscribe; -------------------------------------------------------------------------------- /src/core/genesis.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::str::FromStr; 3 | use std::str::Utf8Error; 4 | 5 | use serde::{Serialize, Serializer, Deserialize, Deserializer}; 6 | use ethereum_types::H160; 7 | use parking_lot::RwLock; 8 | 9 | use cryptocurrency_kit::ethkey::Address; 10 | use cryptocurrency_kit::crypto::EMPTY_HASH; 11 | 12 | use crate::{ 13 | types::{Timestamp, Gas, Difficulty, Height, EMPTY_ADDRESS}, 14 | types::block::{Block, Header}, 15 | types::votes::{decrypt_commit_bytes, encrypt_commit_bytes, Votes}, 16 | types::{Validator, Validators}, 17 | config::GenesisConfig, 18 | common, 19 | }; 20 | use super::{ 21 | ledger::Ledger, 22 | }; 23 | 24 | pub(crate) fn store_genesis_block(genesis_config: &GenesisConfig, ledger: Arc>) -> Result<(), String> { 25 | use chrono::{Local, DateTime, ParseError}; 26 | let mut ledger = ledger.write(); 27 | if let Some(genesis) = ledger.get_genesis_block() { 28 | info!("Genesis hash:{:?}", genesis.hash()); 29 | ledger.reload_meta(); 30 | return Ok(()); 31 | } 32 | // add validators 33 | { 34 | let validators: Validators = genesis_config.validator.iter().map(|validator| { 35 | common::string_to_address(validator).unwrap() 36 | }).map(|address| { 37 | Validator::new(address) 38 | }).collect(); 39 | ledger.add_validators(validators); 40 | } 41 | 42 | // TODO Add more xin 43 | { 44 | let proposer = common::string_to_address(&genesis_config.proposer)?; 45 | let epoch_time: DateTime = { 46 | let epoch_time_str = genesis_config.epoch_time.to_string(); 47 | DateTime::from_str(&epoch_time_str) 48 | }.map_err(|err: ParseError| err.to_string())?; 49 | 50 | let extra = genesis_config.extra.as_bytes().to_vec(); 51 | let mut header = Header::new(EMPTY_HASH, proposer, EMPTY_HASH, EMPTY_HASH, EMPTY_HASH, 52 | 0, 0, 0, genesis_config.gas_used + 10, genesis_config.gas_used, 53 | epoch_time.timestamp() as Timestamp, None, Some(extra)); 54 | let block = Block::new(header, vec![]); 55 | ledger.add_genesis_block(&block); 56 | } 57 | 58 | Ok(()) 59 | } 60 | 61 | #[cfg(test)] 62 | mod test { 63 | use super::*; 64 | use crate::common::random_dir; 65 | use cryptocurrency_kit::ethkey::{Generator, Random}; 66 | use kvdb_rocksdb::Database; 67 | use crate::store::schema::Schema; 68 | use crate::core::ledger::{Ledger, LastMeta}; 69 | use lru_time_cache::LruCache; 70 | 71 | #[test] 72 | fn t_genesis_block() { 73 | let secret = Random.generate().unwrap(); 74 | 75 | let database = Database::open_default(&random_dir()).map_err(|err| err.to_string()).unwrap(); 76 | let schema = Schema::new(Arc::new(database)); 77 | let mut ledger = Ledger::new( 78 | LastMeta::new_zero(), 79 | LruCache::with_capacity(1 << 10), 80 | LruCache::with_capacity(1 << 10), 81 | vec![], 82 | schema, 83 | ); 84 | 85 | let mut header = Header::new(EMPTY_HASH, Address::from(10), EMPTY_HASH, EMPTY_HASH, EMPTY_HASH, 86 | 0, 0, 0, 10, 10, 87 | 192, None, Some(vec![12, 1])); 88 | let block = Block::new(header, vec![]); 89 | 90 | ledger.add_genesis_block(&block); 91 | 92 | assert_eq!(false, ledger.get_block_hash_by_height(0).is_none()); 93 | assert_eq!(true, ledger.get_block_hash_by_height(1).is_none()); 94 | } 95 | 96 | #[test] 97 | fn t_back_block() { 98 | let secret = Random.generate().unwrap(); 99 | 100 | let database = Database::open_default(&random_dir()).map_err(|err| err.to_string()).unwrap(); 101 | let schema = Schema::new(Arc::new(database)); 102 | let mut ledger = Ledger::new( 103 | LastMeta::new_zero(), 104 | LruCache::with_capacity(1 << 10), 105 | LruCache::with_capacity(1 << 10), 106 | vec![], 107 | schema, 108 | ); 109 | 110 | let mut header = Header::new(EMPTY_HASH, Address::from(10), EMPTY_HASH, EMPTY_HASH, EMPTY_HASH, 111 | 0, 0, 0, 10, 10, 112 | 192, None, Some(vec![12, 1])); 113 | let block = Block::new(header, vec![]); 114 | 115 | ledger.add_genesis_block(&block); 116 | ledger.reload_meta(); 117 | 118 | (1_u64..10).for_each(|height|{ 119 | let mut header = Header::new(EMPTY_HASH, Address::from(10), EMPTY_HASH, EMPTY_HASH, EMPTY_HASH, 120 | 0, 0, height, 10, 10, 121 | 192, None, Some(vec![12, 1])); 122 | let block = Block::new(header, vec![]); 123 | 124 | ledger.add_block(&block); 125 | }); 126 | 127 | (1_u64..10).for_each(|height|{ 128 | let block = ledger.get_block_by_height(height).unwrap(); 129 | let block1 = ledger.get_block(&block.hash()).unwrap(); 130 | println!("{:?}", block); 131 | println!("|{:?}", block1); 132 | 133 | }); 134 | 135 | // let schema = ledger.get_schema(); 136 | // for block in schema.blocks().iter() { 137 | // println!("{:?}", block); 138 | // } 139 | // 140 | // println!("last_block {:?}", ledger.get_last_block()); 141 | } 142 | 143 | #[test] 144 | fn t_exists_db() { 145 | // let database = Database::open_default("/tmp/block/c1").map_err(|err| err.to_string()).unwrap(); 146 | // let schema = Schema::new(Arc::new(database)); 147 | // for key in schema.blocks().keys() { 148 | // println!("{:?}", key); 149 | // } 150 | 151 | // for block in schema.blocks().iter() { 152 | // println!("{:?}", block); 153 | // } 154 | 155 | // for value in schema.blocks().values() { 156 | // println!("{:?}", value); 157 | // } 158 | } 159 | } -------------------------------------------------------------------------------- /src/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ledger; 2 | pub mod genesis; 3 | pub mod transaction_pool; 4 | pub mod tx_pool; 5 | pub mod chain; 6 | pub mod actor; 7 | -------------------------------------------------------------------------------- /src/core/transaction_pool.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, LowerHex, Formatter}; 2 | use std::sync::Arc; 3 | 4 | use ethereum_types::U256; 5 | use cryptocurrency_kit::{ 6 | ethkey::Address, 7 | crypto::{CryptoHash, Hash, hash}, 8 | }; 9 | use transaction_pool::VerifiedTransaction; 10 | 11 | 12 | use crate::{ 13 | types::{Gas, Height, Timestamp}, 14 | types::transaction::Transaction, 15 | }; 16 | 17 | #[derive(Debug, Default, Clone)] 18 | pub struct TransactionBuilder { 19 | nonce: U256, 20 | gas_price: Gas, 21 | gas: Gas, 22 | sender: Address, 23 | mem_usage: usize, 24 | } 25 | 26 | impl TransactionBuilder { 27 | pub fn tx(&self) -> Self { 28 | self.clone() 29 | } 30 | 31 | pub fn nonce>(mut self, nonce: T) -> Self { 32 | self.nonce = nonce.into(); 33 | self 34 | } 35 | 36 | pub fn gas_price>(mut self, gas_price: T) -> Self { 37 | self.gas_price = gas_price.into(); 38 | self 39 | } 40 | 41 | pub fn sender>(mut self, sender: T) -> Self { 42 | self.sender = sender.into(); 43 | self 44 | } 45 | 46 | pub fn mem_usage(mut self, mem_usage: usize) -> Self { 47 | self.mem_usage = mem_usage; 48 | self 49 | } 50 | 51 | // pub fn new(self) -> Transaction { 52 | // let hash = self.nonce ^ (U256::from(100) * self.gas_price) ^ (U256::from(100_000) * U256::from(self.sender.low_u64())); 53 | //// Transaction::new() 54 | // } 55 | } 56 | 57 | impl VerifiedTransaction for Transaction { 58 | type Hash = Hash; 59 | type Sender = Address; 60 | fn hash(&self) -> &Hash { self.get_hash().as_ref().unwrap() } 61 | 62 | /// TODO 63 | fn mem_usage(&self) -> usize { 0 } 64 | 65 | fn sender(&self) -> &Address { self.to().unwrap() } 66 | } 67 | 68 | pub type SharedTransaction = Arc; 69 | 70 | #[cfg(test)] 71 | mod tests { 72 | use super::*; 73 | use transaction_pool::Pool; 74 | 75 | 76 | #[test] 77 | fn t_transaction_pool(){ 78 | 79 | } 80 | } -------------------------------------------------------------------------------- /src/core/tx_pool.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::collections::BTreeMap; 3 | 4 | use ::actix::prelude::*; 5 | use priority_queue::PriorityQueue; 6 | use cryptocurrency_kit::crypto::{Hash, hash, EMPTY_HASH}; 7 | use evmap::{self, WriteHandle, ReadHandle}; 8 | 9 | use crate::{ 10 | types::transaction::Transaction, 11 | error::TxPoolError, 12 | }; 13 | 14 | pub const MAX_TXPOOL_SIZE: u64 = 10_000_000; 15 | pub const MAX_SLOT_SIZE: u32 = 1_000; 16 | 17 | pub trait TxPool { 18 | fn len(&self) -> usize; 19 | fn get_tx(&self, tx_hash: &Hash) -> Option<&Transaction>; 20 | fn get_n_tx(&self, n: u64) -> Vec<&Transaction>; 21 | fn add_tx(&mut self, transaction: Transaction) -> Result; 22 | fn add_txs(&mut self, transactions: &Vec) -> Result; 23 | fn remove_txs(&mut self, tx_hashes: Vec<&Hash>); 24 | } 25 | 26 | pub type SafeTxPool = Box; 27 | 28 | pub struct BaseTxPool { 29 | pq: PriorityQueue, 30 | txs: Vec>, 31 | } 32 | 33 | impl Actor for BaseTxPool { 34 | type Context = Context; 35 | } 36 | 37 | impl TxPool for BaseTxPool { 38 | fn len(&self) -> usize { 39 | self.txs.len() 40 | } 41 | 42 | fn get_tx(&self, tx_hash: &Hash) -> Option<&Transaction> { 43 | self.txs[self.get_idx(tx_hash)].get(tx_hash) 44 | } 45 | 46 | fn get_n_tx(&self, n: u64) -> Vec<&Transaction> { 47 | let mut txs = vec![]; 48 | let i: u64 = 0; 49 | for (tx_hash, _) in self.pq.iter() { 50 | if i >= n { 51 | break; 52 | } 53 | let idx = self.get_idx(tx_hash); 54 | 55 | let m = self.txs.get(idx).unwrap(); 56 | txs.push(m.get(&tx_hash).unwrap()); 57 | } 58 | txs 59 | } 60 | 61 | fn add_tx(&mut self, tx: Transaction) -> Result { 62 | let idx = self.get_idx(tx.get_hash().unwrap()); 63 | let v: &mut BTreeMap<_, _> = self.txs.get_mut(idx).unwrap(); 64 | if v.get(&tx.get_hash().unwrap()).is_some() { 65 | return Ok(self.pq.len() as u64); 66 | } 67 | v.insert(tx.get_hash().unwrap().clone(), tx.clone()); 68 | self.pq.push(tx.get_hash().unwrap().clone(), tx.amount()); 69 | Ok(self.pq.len() as u64) 70 | } 71 | 72 | fn add_txs(&mut self, txs: &Vec) -> Result { 73 | let mut start: u64 = 0; 74 | for tx in txs { 75 | self.add_tx(tx.clone())?; 76 | start += 1; 77 | } 78 | Ok(start) 79 | } 80 | 81 | fn remove_txs(&mut self, tx_hashes: Vec<&Hash>) { 82 | tx_hashes.iter().for_each(|tx_hash| { 83 | let idx = self.get_idx(tx_hash); 84 | let m: &mut BTreeMap<_, _> = self.txs.get_mut(idx).unwrap(); 85 | m.remove(tx_hash); 86 | }); 87 | } 88 | } 89 | 90 | impl BaseTxPool { 91 | pub fn new() -> Self { 92 | let n = (MAX_TXPOOL_SIZE / u64::from(MAX_SLOT_SIZE)) as usize; 93 | let mut tx_pool = BaseTxPool { 94 | pq: PriorityQueue::new(), 95 | txs: Vec::with_capacity(n), 96 | }; 97 | (0..n).for_each(|_| { 98 | tx_pool.txs.push(BTreeMap::new()); 99 | }); 100 | tx_pool 101 | } 102 | fn get_idx(&self, tx_hash: &Hash) -> usize { 103 | use ethereum_types::U256; 104 | let u = U256::from(tx_hash.as_ref()); 105 | (u % U256::from(self.txs.len())).as_u64() as usize 106 | } 107 | } 108 | 109 | 110 | #[cfg(test)] 111 | mod tests { 112 | use super::*; 113 | use std::sync::RwLock; 114 | 115 | struct TxPoolActor { 116 | tx_pool: Arc>>, 117 | } 118 | 119 | impl Actor for TxPoolActor { 120 | type Context = Context; 121 | } 122 | 123 | #[test] 124 | fn t_txpool() { 125 | // let mut v = vec![]; 126 | (0..10_0000).for_each(|_idx| {}) 127 | } 128 | } -------------------------------------------------------------------------------- /src/error/mod.rs: -------------------------------------------------------------------------------- 1 | use failure::Error; 2 | 3 | use cryptocurrency_kit::crypto::Hash; 4 | 5 | #[derive(Debug, Fail)] 6 | pub enum TxPoolError { 7 | #[fail(display = "More than max txpool limit, max:{}", _0)] 8 | MoreThanMaxSIZE(u64), 9 | } 10 | 11 | #[derive(Debug, Fail)] 12 | pub enum P2PError { 13 | #[fail(display = "Handshake fail")] 14 | HandShakeFailed, 15 | #[fail(display = "different genesis")] 16 | DifferentGenesis, 17 | #[fail(display = "Dump connected")] 18 | DumpConnected, 19 | #[fail(display = "Invalid Message type")] 20 | InvalidMessage, 21 | #[fail(display = "Timeout")] 22 | Timeout, 23 | } 24 | 25 | pub type ChainResult = Result<(), ChainError>; 26 | 27 | #[derive(Debug, Fail)] 28 | pub enum ChainError { 29 | #[fail(display = "the block has exist, ({:?})", _0)] 30 | Exists(Hash), 31 | #[fail(display = "An unknown error has occurred, ({})", _0)] 32 | Unknown(String), 33 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(custom_attribute)] 2 | #![feature(nll)] 3 | #![feature(vec_remove_item)] 4 | #![feature(get_type_id)] 5 | #![feature(duration_as_u128)] 6 | #![feature(await_macro, futures_api, async_await)] 7 | 8 | extern crate serde; 9 | #[macro_use] 10 | extern crate serde_derive; 11 | #[macro_use] 12 | extern crate serde_json; 13 | extern crate serde_millis; 14 | #[macro_use] 15 | extern crate runtime_fmt; 16 | #[macro_use] 17 | extern crate lazy_static; 18 | #[macro_use] 19 | extern crate ethereum_types; 20 | #[macro_use] 21 | extern crate cryptocurrency_kit; 22 | #[macro_use] 23 | extern crate actix; 24 | extern crate actix_broker; 25 | #[macro_use] 26 | extern crate crossbeam; 27 | #[macro_use] 28 | extern crate log; 29 | extern crate env_logger; 30 | #[macro_use] 31 | extern crate failure; 32 | 33 | pub mod common; 34 | pub mod util; 35 | pub mod consensus; 36 | pub mod types; 37 | pub mod store; 38 | pub mod core; 39 | pub mod protocol; 40 | pub mod p2p; 41 | pub mod error; 42 | pub mod pprof; 43 | #[macro_use] 44 | pub mod subscriber; 45 | pub mod minner; 46 | pub mod cmd; 47 | pub mod config; 48 | pub mod logger; 49 | pub mod mocks; 50 | pub mod api; -------------------------------------------------------------------------------- /src/logger/mod.rs: -------------------------------------------------------------------------------- 1 | use log::Level; 2 | 3 | pub fn init_log() { 4 | env_logger::init(); 5 | info!("👊 logger init successfully"); 6 | } 7 | 8 | pub (crate) fn init_test_env_log() { 9 | use std::env; 10 | use env_logger::{Builder, Target}; 11 | 12 | env::set_var("RUST_LOG", "trace"); 13 | let mut builder = Builder::from_default_env(); 14 | builder.target(Target::Stdout); 15 | builder.init(); 16 | } -------------------------------------------------------------------------------- /src/minner/mod.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crossbeam::scope; 4 | use ::actix::prelude::*; 5 | use actix_broker::{BrokerSubscribe, BrokerIssue}; 6 | use parking_lot::RwLock; 7 | use crossbeam::{Sender, Receiver, channel::bounded}; 8 | use rand::random; 9 | use cryptocurrency_kit::ethkey::{Address, KeyPair}; 10 | use cryptocurrency_kit::storage::values::StorageValue; 11 | use cryptocurrency_kit::crypto::Hash; 12 | use cryptocurrency_kit::crypto::CryptoHash; 13 | use cryptocurrency_kit::crypto::hash; 14 | use tokio_threadpool; 15 | use futures::*; 16 | use futures::sync::oneshot; 17 | 18 | use crate::{ 19 | subscriber::events::ChainEvent, 20 | core::chain::Chain, 21 | core::tx_pool::{TxPool, SafeTxPool}, 22 | consensus::consensus::{Engine, SafeEngine}, 23 | types::{Height, Timestamp}, 24 | types::block::{Block, Header}, 25 | types::transaction::{Transaction, merkle_root_transactions}, 26 | }; 27 | 28 | pub struct Minner { 29 | minter: Address, 30 | key_pair: KeyPair, 31 | chain: Arc, 32 | txpool: Arc>, 33 | engine: Box, 34 | seal_tx: Sender<()>, 35 | seal_rx: Receiver<()>, 36 | mint_height: Height, 37 | worker: tokio_threadpool::ThreadPool, 38 | } 39 | 40 | impl Actor for Minner { 41 | type Context = Context; 42 | 43 | fn started(&mut self, ctx: &mut Self::Context) { 44 | self.subscribe_async::(ctx); 45 | info!("Start minner actor"); 46 | self.chain.post_event(ChainEvent::SyncBlock(self.chain.get_last_height() + 1)); 47 | self.mine(self.seal_rx.clone()); 48 | } 49 | 50 | fn stopped(&mut self, _ctx: &mut Self::Context) { 51 | info!("Minner actor has stoppped"); 52 | } 53 | } 54 | 55 | 56 | impl Handler for Minner { 57 | type Result = (); 58 | fn handle(&mut self, msg: ChainEvent, _ctx: &mut Self::Context) -> Self::Result { 59 | match msg { 60 | ChainEvent::NewHeader(last_header) => { 61 | debug!("Receive a new header event notify, hash:{:?}, height: {:?}", last_header.block_hash(), last_header.height); 62 | if last_header.height >= self.mint_height { 63 | // stop current consensus 64 | self.seal_tx.send(()).unwrap(); 65 | let seal = self.seal_rx.clone(); 66 | self.mine(seal); 67 | } 68 | } 69 | _ => {} 70 | } 71 | } 72 | } 73 | 74 | impl Minner { 75 | pub fn new(minter: Address, 76 | key_pair: KeyPair, 77 | chain: Arc, 78 | txpool: Arc>, 79 | engine: SafeEngine, 80 | tx: Sender<()>, 81 | rx: Receiver<()>) -> Self { 82 | Minner { 83 | minter, 84 | key_pair, 85 | chain, 86 | txpool, 87 | engine, 88 | seal_tx: tx, 89 | seal_rx: rx, 90 | mint_height: 0, 91 | worker: tokio_threadpool::ThreadPool::new(), 92 | } 93 | } 94 | 95 | fn mine(&mut self, abort: Receiver<()>) { 96 | debug!("Ready to mine next block"); 97 | let mut block = self.packet_next_block(); 98 | self.mint_height = block.height(); 99 | match self.engine.seal(&mut block, abort) { 100 | Ok(_) => {} 101 | Err(err) => { 102 | error!("Failed to seal consensus, err: {:?}", err); 103 | } 104 | } 105 | } 106 | 107 | fn packet_next_block(&self) -> Block { 108 | let (next_time, pre_header) = self.next_block(); 109 | let coinbase = self.coinbase_transaction(); 110 | // let mut mock_transactions = generate_batch_transactions(self.key_pair.secret(), self.minter, self.chain.config.chain_id, 200); 111 | // mock_transactions.push(coinbase); 112 | 113 | let pre_hash: Hash = pre_header.block_hash(); 114 | let tx_hash = merkle_root_transactions(vec![coinbase.clone()]); 115 | let extra = Vec::from("Coinse base"); 116 | 117 | let mut header = Header::new_mock(pre_hash, self.minter, tx_hash, pre_header.height + 1, next_time, Some(extra)); 118 | header.cache_hash(None); 119 | Block::new(header, vec![coinbase]) 120 | } 121 | 122 | fn coinbase_transaction(&self) -> Transaction { 123 | let nonce: u64 = random(); 124 | let to = self.minter; 125 | let amount = random::(); 126 | let gas_limit = random::(); 127 | let gas_price = 1_u64; 128 | let payload = Vec::from(chrono::Local::now().to_string()); 129 | 130 | let mut transaction = Transaction::new(nonce, to, amount, gas_limit, gas_price, payload); 131 | transaction.sign(self.chain.config.chain_id, &self.key_pair.secret()); 132 | transaction 133 | } 134 | 135 | fn next_block(&self) -> (u64, Header) { 136 | let pre_block = self.chain.get_last_block(); 137 | let pre_header = pre_block.header(); 138 | let pre_timestamp = pre_header.time; 139 | let next_timestamp = pre_timestamp + self.chain.config.block_period.as_secs(); 140 | let now_timestamp = chrono::Local::now().timestamp() as u64; 141 | trace!("now timestamp: {}, pre_timestamp: {}, next_timestamp: {}", now_timestamp, pre_timestamp, next_timestamp); 142 | if now_timestamp > next_timestamp { 143 | return (now_timestamp, pre_header.clone()); 144 | } 145 | (next_timestamp, pre_header.clone()) 146 | } 147 | } 148 | 149 | #[cfg(test)] 150 | mod test { 151 | use super::*; 152 | use cryptocurrency_kit::ethkey::{Random, Generator}; 153 | 154 | #[test] 155 | fn t_basecoin() { 156 | let nonce: u64 = random(); 157 | let to = Address::from(199); 158 | let amount = random::(); 159 | let gas_limit = random::(); 160 | let gas_price = 1_u64; 161 | let payload = Vec::from(chrono::Local::now().to_string()); 162 | 163 | let mut transaction = Transaction::new(nonce, to, amount, gas_limit, gas_price, payload); 164 | transaction.sign(100, Random.generate().unwrap().secret()); 165 | 166 | let coinbase = transaction; 167 | let tx_hash = merkle_root_transactions(vec![coinbase.clone()]); 168 | println!("coin base hash: {:?}", tx_hash); 169 | } 170 | } -------------------------------------------------------------------------------- /src/mocks/mock_config.toml: -------------------------------------------------------------------------------- 1 | chain_id = 10 2 | ip = "127.0.0.1" 3 | port = 7691 4 | block_period = 3000 # ms 5 | request_time = 3000 # ms 6 | peer_id = "QmbBr2fHwLFKvHkAq1BpbEr4dvR8P6orQxHkVaxeJsJiW8" 7 | ttl = 3000 8 | store = "/tmp/block/c1" 9 | secret = "7f3b0a324e13e5358c3fd686737acd7adf2e5556084ec6d9e48b497082b7ef98" 10 | 11 | [genesis] 12 | validator = ["0x7193d8f91724b39f10cc81e94934c187fa257277", "0x93908f59c6eff007d228398349214acb6b4ac9a4", "0x72d5c75fd6703414aa87f79b3e4797dd09cd9251", "0x58096d35c7a8ff67eba159f33cea7740fc9a737c","0xc759616c865d349ec2afced268fc6f33ff7414a4"] 13 | epoch_time = 2018-09-09T09:09:09.09-09:09 14 | proposer = "0x5701fbd05e77cac003a6894e4b2a3c12287ed313" 15 | gas_used = 10000 16 | extra = "Hello Word!" 17 | [genesis.accounts] 18 | 0x5701fbd05e77cac003a6894e4b2a3c12287ed313 = 500000 19 | 0x6510f8d84c0b8b3091fc3abe2fdff6036c90865d = 500000 20 | 0x3140bda54df92f9453b487afdb3bcce02d154c74 = 500000 21 | 0x7035dafbeac1792ab5b7ed5c903ac63522eb534a = 500000 22 | 0x6730933a2cb6f26af786d7f5979efbdf29049c3a = 500000 23 | 0xfb1bbe89190c9793aec79713e35bdd82a7e5b08b = 500000 24 | 0x1f6f0d11339b5a0db7cef22ae278c15d55178faf = 500000 25 | 0x17f3309f405f53ae3e3c7e98533c58aa0c8c9417 = 500000 26 | 0x6e6e4a7aa7cedac4c4f3e8a7cd363e5f3208e8a6 = 500000 27 | 0x91b73cc738754c4fd7d6a2f0b6b354e293177c80 = 500000 -------------------------------------------------------------------------------- /src/mocks/mod.rs: -------------------------------------------------------------------------------- 1 | use toml::value::Value as Toml; 2 | 3 | use std::fs::{self, File}; 4 | use std::env; 5 | 6 | use crate::config::Config; 7 | 8 | pub(crate) mod utils; 9 | 10 | 11 | pub(crate) fn t_config() -> Config { 12 | let s = env::current_dir().unwrap().to_string_lossy().to_string() + &"/src/mocks/mock_config.toml".to_owned(); 13 | println!("--> {:?}", s); 14 | let config: Config = toml::from_str(&fs::read_to_string(s).unwrap()).unwrap(); 15 | config 16 | } 17 | 18 | #[cfg(test)] 19 | mod test { 20 | use super::*; 21 | 22 | #[test] 23 | fn t_() { 24 | let config = t_config(); 25 | println!("{:?}", config); 26 | } 27 | } -------------------------------------------------------------------------------- /src/mocks/utils.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laohanlinux/consensus-rs/ebf0879ea3cf653c62ac79ff658c804a3bddcc6f/src/mocks/utils.rs -------------------------------------------------------------------------------- /src/p2p/codec.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::io; 3 | 4 | use byteorder::{BigEndian, ByteOrder}; 5 | use bytes::{BufMut, BytesMut}; 6 | use cryptocurrency_kit::storage::values::StorageValue; 7 | use tokio::codec::{Decoder, Encoder}; 8 | 9 | use super::protocol::*; 10 | 11 | pub const MAX_MSG_SIZE: u32 = 1 << 10; 12 | pub const MSG_SIZE: u32 = 4; // byte 13 | 14 | // |msg_size: 4bytes| msg encode | 15 | pub struct MsgPacketCodec; 16 | 17 | impl Decoder for MsgPacketCodec { 18 | type Item = RawMessage; 19 | type Error = io::Error; 20 | 21 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 22 | let size = { 23 | if src.len() < MSG_SIZE as usize { 24 | // continue read 25 | return Ok(None); 26 | } 27 | BigEndian::read_u32(src.as_ref()) 28 | }; 29 | 30 | if src.len() >= (size + MSG_SIZE) as usize { 31 | src.split_to(MSG_SIZE as usize); 32 | let buf = src.split_to(size as usize); 33 | let raw_message: RawMessage = RawMessage::from_bytes(Cow::from(buf.to_vec())); 34 | Ok(Some(raw_message)) 35 | } else { 36 | Ok(None) 37 | } 38 | } 39 | } 40 | 41 | impl Encoder for MsgPacketCodec { 42 | type Item = RawMessage; 43 | type Error = io::Error; 44 | 45 | fn encode(&mut self, msg: RawMessage, dst: &mut BytesMut) -> Result<(), Self::Error> { 46 | let msg = msg.into_bytes(); 47 | let size = msg.len() as u32; 48 | dst.reserve((size + MSG_SIZE) as usize); 49 | dst.put_u32_be(size); 50 | dst.put(msg); 51 | Ok(()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/p2p/config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Default)] 2 | pub struct Config { 3 | pub max_inbound: u64, 4 | pub max_outbound: u64, 5 | pub max_connection_size: u64, 6 | pub seal: bool, 7 | 8 | } 9 | 10 | impl Config { 11 | // pub fn new(max_inbound: u64, max_outbound: u64, max_connection_size: u64) -> Self { 12 | // Config { 13 | // max_inbound, 14 | // max_outbound, 15 | // max_connection_size, 16 | // } 17 | // } 18 | } -------------------------------------------------------------------------------- /src/p2p/discover_service.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Write}; 2 | use std::time::Duration; 3 | 4 | #[macro_use] 5 | use ::actix::prelude::*; 6 | use futures::future; 7 | use futures::prelude::*; 8 | use libp2p::core::PublicKey; 9 | use libp2p::mdns::{MdnsPacket, MdnsService}; 10 | use libp2p::multiaddr::{Multiaddr, ToMultiaddr}; 11 | use libp2p::PeerId; 12 | 13 | #[macro_use] 14 | use crate::subscriber::*; 15 | 16 | pub struct DiscoverService { 17 | p2p_pid: Addr, 18 | } 19 | 20 | impl Actor for DiscoverService { 21 | type Context = Context; 22 | 23 | fn started(&mut self, _ctx: &mut Context) { 24 | trace!("Discover service started"); 25 | } 26 | 27 | fn stopped(&mut self, _ctx: &mut Self::Context) { 28 | trace!("Discover service stopped"); 29 | } 30 | } 31 | 32 | impl DiscoverService { 33 | pub fn spawn_discover_service( 34 | p2p_subscriber: Addr, 35 | peer_id: PeerId, 36 | local_address: Multiaddr, 37 | ttl: Duration, 38 | ) -> Addr { 39 | let mut service = MdnsService::new().expect("Error while creating mDNS service"); 40 | let p2p_subscriber_clone = p2p_subscriber.clone(); 41 | let future = futures::future::poll_fn(move || -> Poll<(), io::Error> { 42 | loop { 43 | let packet = match service.poll() { 44 | Async::Ready(packet) => packet, 45 | Async::NotReady => return Ok(Async::NotReady), 46 | }; 47 | match packet { 48 | MdnsPacket::Query(query) => { 49 | query 50 | .respond(peer_id.clone(), vec![local_address.clone()], ttl) 51 | .unwrap(); 52 | } 53 | MdnsPacket::Response(response) => { 54 | let _peers_size = response.discovered_peers().count(); 55 | for peer in response.discovered_peers() { 56 | let id = peer.id().clone(); 57 | if peer_id.clone() == id { 58 | continue; 59 | } 60 | let mut addresses: Vec = Vec::new(); 61 | for address in peer.addresses() { 62 | addresses.push(address.clone()); 63 | } 64 | // trace!("Get a message from mDNS, local-id:{:?}, remote-id:{:?}", peer_id, id); 65 | // if the receiver actor's mailbox is full, ignore message 66 | p2p_subscriber_clone.try_send(P2PEvent::AddPeer(id, addresses)); 67 | } 68 | } 69 | MdnsPacket::ServiceDiscovery(query) => { 70 | query.respond(ttl); 71 | } 72 | } 73 | } 74 | }); 75 | 76 | Arbiter::spawn(future.then(|_res| { 77 | trace!("mDNS service exit"); 78 | future::result(Ok(())) 79 | })); 80 | 81 | trace!("Create mDSN service successfully"); 82 | let p2p_subscriber = p2p_subscriber.clone(); 83 | Actor::create(|_| DiscoverService { 84 | p2p_pid: p2p_subscriber, 85 | }) 86 | } 87 | } 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::*; 92 | use rand::Rng; 93 | use std::io::{self, Write}; 94 | 95 | pub struct Ping {} 96 | 97 | impl Message for Ping { 98 | type Result = (); 99 | } 100 | 101 | type PongRecipient = Recipient; 102 | 103 | type PongRecipients = Vec>; 104 | 105 | struct Worker {} 106 | 107 | impl Actor for Worker { 108 | type Context = Context; 109 | } 110 | 111 | impl Handler for Worker { 112 | type Result = (); 113 | fn handle(&mut self, msg: P2PEvent, _ctx: &mut Self::Context) { 114 | match msg { 115 | P2PEvent::AddPeer(_, _) => { 116 | writeln!( 117 | io::stdout(), 118 | "{} work receive a msg: {:?}", 119 | chrono::Local::now(), 120 | msg 121 | ); 122 | } 123 | P2PEvent::DropPeer(_, _) => { 124 | writeln!(io::stdout(), "work receive a msg: {:?}", msg); 125 | } 126 | } 127 | } 128 | } 129 | 130 | #[test] 131 | fn t_discover_service() { 132 | crate::logger::init_test_env_log(); 133 | let system = System::new("test"); 134 | let p2p_subscriber = spawn_sync_subscriber(); 135 | let worker_pid = Worker::create(|_| Worker {}); 136 | // register 137 | { 138 | let recipient = worker_pid.recipient(); 139 | // register 140 | let message = SubscribeMessage::SubScribe(recipient); 141 | p2p_subscriber.do_send(message); 142 | } 143 | 144 | let mut mdns = vec![]; 145 | (0..50).for_each(|_| { 146 | let peer_id = PeerId::random(); 147 | let port = rand::random::(); 148 | let address: Multiaddr = format!("/ip4/127.0.0.1/tcp/{}", port).parse().unwrap(); 149 | let pid = DiscoverService::spawn_discover_service( 150 | p2p_subscriber.clone(), 151 | peer_id, 152 | address, 153 | Duration::from_secs(3), 154 | ); 155 | mdns.push(pid); 156 | }); 157 | 158 | crate::util::TimerRuntime::new(Duration::from_secs(3)); 159 | 160 | system.run(); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/p2p/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod discover_service; 3 | pub mod node; 4 | pub mod server; 5 | pub mod session; 6 | pub mod codec; 7 | pub mod protocol; 8 | #[macro_use] 9 | pub use crate::subscriber::*; -------------------------------------------------------------------------------- /src/p2p/node.rs: -------------------------------------------------------------------------------- 1 | use ::actix::prelude::*; 2 | 3 | use super::{ 4 | server::{TcpServer, ServerEvent}, 5 | discover_service::DiscoverService, 6 | }; 7 | 8 | pub struct Node { 9 | server: Addr, 10 | discover_service: Addr, 11 | } 12 | 13 | 14 | impl Actor for Node { 15 | type Context = Context; 16 | 17 | fn started(&mut self, _ctx: &mut Self::Context) { 18 | info!("Node actor has started"); 19 | } 20 | 21 | fn stopped(&mut self, _ctx: &mut Self::Context) { 22 | info!("Node actor has stopped"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/p2p/p2p/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "p2p" 3 | version = "0.1.0" 4 | authors = ["Rg "] 5 | 6 | [dependencies] 7 | farmhash = "1.1.5" 8 | actix = "0.6.0" 9 | byteorder = "*" 10 | bytes = "*" 11 | serde = "1.0" 12 | serde_json = "1.0" 13 | serde_derive = "1.0" 14 | log = "^0.3" 15 | rand = "^0.3" 16 | rustc-serialize = "^0.3" 17 | 18 | futures = "0.1" 19 | tokio = "0.1" 20 | tokio-io = "0.1" 21 | tokio-tcp = "0.1" 22 | 23 | -------------------------------------------------------------------------------- /src/p2p/p2p/doc/Sequence Diagram0.asta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laohanlinux/consensus-rs/ebf0879ea3cf653c62ac79ff658c804a3bddcc6f/src/p2p/p2p/doc/Sequence Diagram0.asta -------------------------------------------------------------------------------- /src/p2p/p2p/doc/Sequence Diagram0.asta.bak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laohanlinux/consensus-rs/ebf0879ea3cf653c62ac79ff658c804a3bddcc6f/src/p2p/p2p/doc/Sequence Diagram0.asta.bak -------------------------------------------------------------------------------- /src/p2p/p2p/doc/roadmap: -------------------------------------------------------------------------------- 1 | 1: ping-pong 2 | 2: Direct Send Message 3 | 3: Broadcast -------------------------------------------------------------------------------- /src/p2p/p2p/src/client.rs: -------------------------------------------------------------------------------- 1 | use actix::prelude::*; 2 | use futures::Future; 3 | use std::str::FromStr; 4 | use std::time::Duration; 5 | use std::{io,io::Write, net, process, thread}; 6 | use tokio_io::codec::FramedRead; 7 | use tokio_io::io::WriteHalf; 8 | use tokio_io::AsyncRead; 9 | use tokio_tcp::TcpStream; 10 | 11 | use codec::{Codec, OutboundCode, Request, RequestPayload, Response, ResponsePayload, TId, TAddr, TValue}; 12 | use kad::base::Node; 13 | 14 | pub struct Client { 15 | pub node: Node, 16 | pub framed: actix::io::FramedWrite, OutboundCode>, 17 | } 18 | 19 | #[derive(Message)] 20 | struct ClientCommand(String); 21 | 22 | impl Actor for Client { 23 | type Context = Context; 24 | fn started(&mut self, ctx:&mut Context) { 25 | writeln!(io::stdout(), "client started...").unwrap(); 26 | self.hb(ctx) 27 | } 28 | 29 | fn stopping(&mut self, _: &mut Context) ->Running { 30 | println!("Disconnectd"); 31 | System::current().stop(); 32 | Running::Stop 33 | } 34 | } 35 | 36 | impl Client { 37 | fn hb(&self, ctx: &mut Context) { 38 | let node = self.node.clone(); 39 | ctx.run_later(Duration::new(1,0), move |act, ctx| { 40 | writeln!(io::stdout(), "client send a ping").unwrap(); 41 | let req = Request::new(node, 100, RequestPayload::Ping); 42 | act.framed.write(req); 43 | act.hb(ctx); 44 | }); 45 | } 46 | } 47 | 48 | impl Handler> for Client { 49 | type Result = Response; 50 | 51 | fn handle(&mut self, msg: Request, ctx: &mut Context) -> Response { 52 | let node = self.node.clone(); 53 | let req = Request::new(node.clone(), 100, RequestPayload::Ping); 54 | self.framed.write(msg); 55 | let resp = Response::new(req, node, ResponsePayload::NoResult); 56 | 57 | resp 58 | } 59 | } 60 | 61 | impl actix::io::WriteHandler for Client {} 62 | 63 | /// Server communication 64 | impl StreamHandler, io::Error> for Client { 65 | fn handle( 66 | &mut self, msg: io::Result>>, ctx: &mut Context, 67 | ) { 68 | println!("receive server msg: {:?}", msg); 69 | // match msg { 70 | // Ok(Some(codec::ChatResponse::Message(ref msg))) => { 71 | // println!("message: {}", msg); 72 | // } 73 | // Ok(Some(codec::ChatResponse::Joined(ref msg))) => { 74 | // println!("!!! joined: {}", msg); 75 | // } 76 | // Ok(Some(codec::ChatResponse::Rooms(rooms))) => { 77 | // println!("\n!!! Available rooms:"); 78 | // for room in rooms { 79 | // println!("{}", room); 80 | // } 81 | // println!(); 82 | // } 83 | // _ => ctx.stop(), 84 | // } 85 | } 86 | } -------------------------------------------------------------------------------- /src/p2p/p2p/src/codec.rs: -------------------------------------------------------------------------------- 1 | use actix::prelude::*; 2 | use byteorder::{BigEndian, ByteOrder}; 3 | use bytes::{BufMut, BytesMut}; 4 | use serde_json as json; 5 | use tokio_io::codec::{Decoder, Encoder}; 6 | use actix::prelude::*; 7 | use actix::dev::{MessageResponse, ResponseChannel}; 8 | use kad::base::Node; 9 | use std::net; 10 | use std::io; 11 | use std::marker::PhantomData; 12 | 13 | pub type TId = u64; 14 | pub type TAddr = net::SocketAddr; 15 | pub type TValue = Vec; 16 | pub type TData = Vec; 17 | 18 | #[derive(Serialize, Deserialize, Debug, Clone)] 19 | pub enum RawMessage { 20 | P2P(P2PMessage), 21 | } 22 | 23 | impl RawMessage { 24 | pub fn new_p2p_request(req: Request) -> RawMessage{ 25 | let p2p_message = P2PMessage::Req(req); 26 | let raw_message = RawMessage::P2P(p2p_message); 27 | raw_message 28 | } 29 | 30 | pub fn new_p2p_response(resp: Response) ->RawMessage { 31 | let p2p_message = P2PMessage::Resp(resp); 32 | let raw_message = RawMessage::P2P(p2p_message); 33 | raw_message 34 | } 35 | } 36 | 37 | #[derive(Serialize, Deserialize, Debug, Clone)] 38 | pub enum P2PMessage { 39 | Req(Request), 40 | Resp(Response), 41 | } 42 | 43 | #[derive(Serialize, Deserialize, Debug, Clone)] 44 | pub struct Request { 45 | pub caller: Node, 46 | pub request_id: TId, 47 | pub payload: RequestPayload, 48 | } 49 | 50 | impl Request { 51 | pub fn new(node: Node, request_id: TId, payload: RequestPayload) 52 | -> Request{ 53 | Request { 54 | caller: node, 55 | request_id, 56 | payload, 57 | } 58 | } 59 | } 60 | 61 | impl Message for Request { 62 | type Result = Response; 63 | } 64 | 65 | /// Payload in the request. 66 | #[derive(Serialize, Deserialize, Debug, Clone, Message)] 67 | pub enum RequestPayload { 68 | Ping, 69 | FindNode(TId), 70 | FindValue(TId), 71 | Store(TId, TValue) 72 | } 73 | 74 | /// Payload in the response. 75 | #[derive(Serialize, Deserialize, Debug, Clone, Message)] 76 | pub enum ResponsePayload { 77 | NodesFound(Vec>), 78 | ValueFound(TValue), 79 | NoResult 80 | } 81 | 82 | /// Response structure. 83 | #[derive(Serialize, Deserialize, Debug, Clone)] 84 | pub struct Response { 85 | pub request: Request, 86 | pub responder: Node, 87 | pub payload: ResponsePayload 88 | } 89 | 90 | impl Response { 91 | pub fn new(request: Request, node: Node, payload: ResponsePayload) -> Response { 92 | Response{ 93 | request, 94 | responder: node, 95 | payload, 96 | } 97 | } 98 | } 99 | 100 | impl MessageResponse for Response 101 | where A: Actor, 102 | M:Message> 103 | { 104 | fn handle>(self, _:&mut A::Context, tx: Option) { 105 | if let Some(tx) = tx { 106 | tx.send(self); 107 | } 108 | } 109 | } 110 | 111 | /// |2Byte|xxxx| 112 | /// |msg Size|xxxx| 113 | pub struct RawCodec; 114 | 115 | impl Decoder for RawCodec { 116 | type Item = RawMessage; 117 | type Error = io::Error; 118 | 119 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 120 | let size = { 121 | if src.len() < 2 { 122 | return Ok(None); 123 | } 124 | BigEndian::read_u16(src.as_ref()) as usize 125 | }; 126 | if src.len() >= size + 2 { 127 | src.split_to(2); 128 | let buf = src.split_to(size); 129 | Ok(Some(json::from_slice::>(&buf)?)) 130 | }else { 131 | Ok(None) 132 | } 133 | } 134 | } 135 | 136 | /// |2Byte|xxxx| 137 | /// |msg Size|xxxx| 138 | //pub struct InboundCodec; 139 | pub struct Codec; 140 | 141 | impl Decoder for Codec { 142 | type Item = Request; 143 | type Error = io::Error; 144 | 145 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 146 | let size = { 147 | if src.len() < 2 { 148 | return Ok(None); 149 | } 150 | BigEndian::read_u16(src.as_ref()) as usize 151 | }; 152 | if src.len() >= size + 2 { 153 | src.split_to(2); 154 | let buf = src.split_to(size); 155 | Ok(Some(json::from_slice::>(&buf)?)) 156 | }else { 157 | Ok(None) 158 | } 159 | } 160 | } 161 | 162 | impl Encoder for RawCodec { 163 | type Item = RawMessage; 164 | type Error = io::Error; 165 | fn encode(&mut self, msg: RawMessage, 166 | dst: &mut BytesMut) -> Result<(), Self::Error> { 167 | let msg = json::to_string(&msg).unwrap(); 168 | let msg_ref: &[u8] = msg.as_ref(); 169 | 170 | dst.reserve(msg_ref.len() + 2); 171 | dst.put_u16_be(msg_ref.len() as u16); 172 | dst.put(msg_ref); 173 | Ok(()) 174 | } 175 | } 176 | 177 | impl Encoder for Codec { 178 | type Item = Response; 179 | type Error = io::Error; 180 | fn encode(&mut self, msg: Response, 181 | dst: &mut BytesMut) -> Result<(), Self::Error> { 182 | let msg = json::to_string(&msg).unwrap(); 183 | let msg_ref: &[u8] = msg.as_ref(); 184 | 185 | dst.reserve(msg_ref.len() + 2); 186 | dst.put_u16_be(msg_ref.len() as u16); 187 | dst.put(msg_ref); 188 | Ok(()) 189 | } 190 | } 191 | 192 | // client 193 | pub struct OutboundCode; 194 | 195 | impl Decoder for OutboundCode { 196 | type Item = Response; 197 | type Error = io::Error; 198 | 199 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 200 | let size = { 201 | if src.len() > 2 { 202 | return Ok(None); 203 | } 204 | BigEndian::read_u16(src.as_ref()) as usize 205 | }; 206 | if src.len() >= size +2 { 207 | src.split_to(2); 208 | let buf = src.split_to(size); 209 | Ok(Some(json::from_slice::>(&buf)?)) 210 | }else { 211 | Ok(None) 212 | } 213 | } 214 | } 215 | 216 | impl Encoder for OutboundCode { 217 | type Item = Request; 218 | type Error = io::Error; 219 | 220 | fn encode(&mut self, msg: Request, 221 | dst: &mut BytesMut) -> Result<(), Self::Error> { 222 | let msg = json::to_string(&msg).unwrap(); 223 | let msg_ref: &[u8] = msg.as_ref(); 224 | 225 | dst.reserve(msg_ref.len() + 2); 226 | dst.put_u16_be(msg_ref.len() as u16); 227 | dst.put(msg_ref); 228 | Ok(()) 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/p2p/p2p/src/codec2.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laohanlinux/consensus-rs/ebf0879ea3cf653c62ac79ff658c804a3bddcc6f/src/p2p/p2p/src/codec2.rs -------------------------------------------------------------------------------- /src/p2p/p2p/src/kad/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Dmitry "Divius" Tantsur 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | // 9 | 10 | //! Distributed Hash Table. 11 | //! 12 | //! The goal of this project is to provide flexible implementation of DHT 13 | //! for different kind of Rust applications. There will be loosely coupled parts: 14 | //! 15 | //! 1. DHT neighborhood table implementation, will be represented by 16 | //! `GenericNodeTable` trait and `KNodeTable` implementation. 17 | //! 2. Generic DHT logic implementation in `Service` and `service::Handler` 18 | //! structures. 19 | //! 3. Generic bits for implementing protocols in `service::Handler` structure 20 | //! and `protocol` module. 21 | //! 4. (In the future) simple implementations for testing purposes. 22 | 23 | pub use base::GenericId; 24 | pub use base::GenericNodeTable; 25 | pub use base::Node; 26 | pub use knodetable::KNodeTable; 27 | pub use service::Service; 28 | 29 | pub mod base; 30 | pub mod knodetable; 31 | pub mod protocol; 32 | pub mod service; 33 | pub mod utils; 34 | -------------------------------------------------------------------------------- /src/p2p/p2p/src/kad/protocol.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Dmitry "Divius" Tantsur 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Generic protocol bits for implementing custom protocols. 10 | 11 | use super::base::{GenericId, Node}; 12 | 13 | 14 | /// Payload in the request. 15 | pub enum RequestPayload { 16 | Ping, 17 | FindNode(TId), 18 | FindValue(TId), 19 | Store(TId, TValue) 20 | } 21 | 22 | /// Request structure. 23 | pub struct Request { 24 | pub caller: Node, 25 | pub request_id: TId, 26 | pub payload: RequestPayload 27 | } 28 | 29 | /// Payload in the response. 30 | pub enum ResponsePayload { 31 | NodesFound(Vec>), 32 | ValueFound(TValue), 33 | NoResult 34 | } 35 | 36 | /// Response structure. 37 | pub struct Response { 38 | pub request: Request, 39 | pub responder: Node, 40 | pub payload: ResponsePayload 41 | } 42 | 43 | /// Trait for a protocol implementation. 44 | pub trait Protocol : Send { 45 | /// Value type. 46 | type Id: GenericId; 47 | type Addr: Send + Sync; 48 | type Value: Send + Sync; 49 | /// Parse request from binary data. 50 | fn parse_request(&self, data: &[u8]) -> Request; 51 | /// Format response to binary data. 52 | fn format_response(&self, Response) -> Vec; 53 | } 54 | -------------------------------------------------------------------------------- /src/p2p/p2p/src/kad/utils.rs: -------------------------------------------------------------------------------- 1 | //! Various utilities 2 | 3 | 4 | #[cfg(test)] 5 | pub mod test { 6 | use std::net; 7 | 8 | use super::super::Node; 9 | 10 | /* 11 | pub type IdType = u64; 12 | pub fn make_id(i: u8) -> IdType { 13 | i as IdType 14 | }*/ 15 | pub type IdType = Vec; 16 | pub fn make_id(i: u8) -> IdType { 17 | vec![i] 18 | } 19 | 20 | pub static ADDR: &'static str = "127.0.0.1:8008"; 21 | 22 | pub fn new_node(id: IdType) -> Node { 23 | new_node_with_port(id, 8008) 24 | } 25 | 26 | pub fn new_node_with_port(id: IdType, port: u16) -> Node { 27 | Node { 28 | id: id, 29 | address: net::SocketAddr::V4(net::SocketAddrV4::new( 30 | net::Ipv4Addr::new(127, 0, 0, 1), 31 | port 32 | )) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/p2p/p2p/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate farmhash; 2 | extern crate byteorder; 3 | extern crate bytes; 4 | extern crate serde; 5 | extern crate serde_json; 6 | extern crate tokio; 7 | extern crate tokio_io; 8 | extern crate tokio_tcp; 9 | extern crate rand; 10 | extern crate rustc_serialize; 11 | extern crate futures; 12 | 13 | #[macro_use] 14 | extern crate log; 15 | 16 | #[macro_use] 17 | extern crate serde_derive; 18 | 19 | #[macro_use] 20 | extern crate actix; 21 | 22 | use std::net; 23 | use std::marker; 24 | 25 | use kad::*; 26 | use kad::protocol::*; 27 | 28 | mod peer; 29 | mod codec; 30 | mod codec2; 31 | mod session; 32 | mod server; 33 | mod client; 34 | pub mod kad; 35 | 36 | pub struct P2PProtocol{} 37 | 38 | impl Protocol for P2PProtocol { 39 | type Id = u64; 40 | type Addr = net::SocketAddr; 41 | type Value = u64; 42 | /// Parse request from binary data. 43 | fn parse_request(&self, data: &[u8]) -> Request { 44 | let address = "127.0.0.1:800"; 45 | let id = farmhash::hash64(address.as_bytes()); 46 | let node = Node{address: address.parse().unwrap(), id: id}; 47 | Request{ 48 | caller: node, 49 | request_id: id, 50 | payload: RequestPayload::Ping, 51 | } 52 | } 53 | 54 | /// Format response to binary data. 55 | fn format_response(&self, resp: Response) -> Vec { 56 | vec![] 57 | } 58 | } 59 | fn ping_callback(node: &Node, flag: bool) {} 60 | 61 | fn find_node(node: Vec>) {} 62 | 63 | fn find_value(value: Option, nodes: Vec>) { 64 | 65 | } 66 | 67 | //impl GenericNodeTable for WhisperNodeTable { 68 | // fn random_id(&self) ->WhisperNodeTable{ 69 | // WhisperNodeTable{ 70 | // node: None, 71 | // } 72 | // } 73 | // 74 | // // 更新节点 75 | // fn update(&mut self, node: &Node) -> bool { 76 | // match self.node { 77 | // Some(..) => false, 78 | // None => { 79 | // self.node = Some(node.clone()); 80 | // true 81 | // } 82 | // } 83 | // } 84 | // 85 | // fn find(&self, id: &u64, _count: usize) ->Vec>{ 86 | // if let Some(ref node) = self.node { 87 | // if node.id == *id { 88 | // vec![node.clone()] 89 | // }else { 90 | // vec![] 91 | // } 92 | // }else { 93 | // vec![] 94 | // } 95 | // } 96 | // 97 | // fn pop_oldest(&mut self) -> Vec>{ 98 | // let result = self.node.or_else(||vec![]).unwrap(); 99 | // self.node = Node; 100 | // result 101 | // } 102 | // 103 | //} 104 | 105 | #[cfg(test)] 106 | mod tests { 107 | #[test] 108 | fn it_works() { 109 | assert_eq!(2 + 2, 4); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/p2p/p2p/src/peer.rs: -------------------------------------------------------------------------------- 1 | use actix::prelude::*; 2 | 3 | use std::net; 4 | use super::base::{Node}; 5 | 6 | use std::collections::{HashMap}; 7 | 8 | pub struct Connect { 9 | pub addr: Node, 10 | } 11 | impl actix::Message for Connect { 12 | type Result = usize; 13 | } 14 | 15 | #[derive(Message)] 16 | pub struct Disconnect{ 17 | pub id: usize, 18 | } 19 | 20 | 21 | /// Send message to specific room 22 | #[derive(Message)] 23 | pub struct Message { 24 | /// Id of the client session 25 | pub id: usize, 26 | /// Peer message 27 | pub msg: String, 28 | /// Room name 29 | pub room: String, 30 | } 31 | 32 | pub struct P2PServer { 33 | sessions: HashMap>, 34 | } 35 | 36 | impl Default for P2PServer { 37 | fn default() -> P2PServer { 38 | P2PServer { 39 | sessions: HashMap::new(), 40 | } 41 | } 42 | } 43 | 44 | impl P2PServer { 45 | 46 | fn broadcast_message(&self, message: &str) { 47 | 48 | } 49 | 50 | fn send_message(&self, id: u64) {} 51 | } -------------------------------------------------------------------------------- /src/p2p/p2p/src/session.rs: -------------------------------------------------------------------------------- 1 | use actix::prelude::*; 2 | use std::io::{self, Write}; 3 | use std::net; 4 | use std::time::{Duration, Instant}; 5 | use tokio_io::io::WriteHalf; 6 | use tokio_tcp::TcpStream; 7 | 8 | use codec::{Request, Response, RequestPayload, ResponsePayload, 9 | RawCodec, RawMessage, P2PMessage, TId, TAddr, TValue}; 10 | 11 | use server::{Server, Disconnect}; 12 | use kad::base::{GenericId, Node}; 13 | 14 | type RequestType = Request; 15 | type ResponseType = Response; 16 | 17 | #[derive(Message)] 18 | pub struct Message(pub String); 19 | 20 | pub struct Session { 21 | as_peer: bool, 22 | 23 | // local node id 24 | node: Node, 25 | 26 | /// this is address of chat server 27 | addr: Addr, 28 | /// Client must send ping at least once per 10 seconds, otherwise we drop 29 | /// connection 30 | hb: Instant, 31 | /// Framed wrapper 32 | framed: actix::io::FramedWrite, RawCodec>, 33 | } 34 | 35 | impl Actor for Session { 36 | type Context = actix::Context; 37 | 38 | fn started(&mut self, ctx: &mut Self::Context) { 39 | writeln!(io::stdout(), "started a session actor(as peer-{}) node: {:?}", self.as_peer, self.node).unwrap(); 40 | if self.as_peer { 41 | self.send_hb(ctx); 42 | } 43 | } 44 | 45 | fn stopping(&mut self, _: &mut Self::Context) -> Running { 46 | /// notify server 47 | self.addr.do_send(Disconnect{id: self.node.id}); 48 | Running::Stop 49 | } 50 | } 51 | 52 | impl actix::io::WriteHandler for Session{} 53 | 54 | /// To use `Framed` with an actor, we have to implement `StreamHandler` trait 55 | impl StreamHandler, io::Error> for Session { 56 | /// This is main event loop for client requests 57 | fn handle(&mut self, msg: io::Result>>, ctx: &mut Self::Context) { 58 | let msg = msg.unwrap(); 59 | writeln!(io::stdout(), "session:{} receive client request msg", self.node.address.to_string()).unwrap(); 60 | if msg.is_none() { 61 | return 62 | } 63 | 64 | let msg = msg.unwrap(); 65 | match msg { 66 | RawMessage::P2P(p2p_msg) =>{ 67 | // self.proccess_raw_msg(p2p_msg,&mut ctx); 68 | }, 69 | _ => unimplemented!() 70 | } 71 | } 72 | } 73 | 74 | impl Session { 75 | pub fn new( 76 | node: Node, 77 | addr: Addr, 78 | framed: actix::io::FramedWrite, RawCodec>, 79 | ) -> Session { 80 | Session { 81 | as_peer: false, 82 | node, 83 | addr, 84 | framed, 85 | hb: Instant::now(), 86 | } 87 | } 88 | 89 | pub fn new_peer( 90 | node:Node, 91 | addr: Addr, 92 | framed:actix::io::FramedWrite, RawCodec>, 93 | ) -> Session { 94 | let mut ses = Session::new(node, addr, framed); 95 | ses.as_peer = true; 96 | ses 97 | } 98 | 99 | /// helper method that sends ping to client every second. 100 | /// 101 | /// also this method check heartbeats from client 102 | fn hb(&self, ctx: &mut actix::Context) { 103 | // TODO 104 | let node = self.node.clone(); 105 | ctx.run_later(Duration::new(1, 0), move |act, ctx|{ 106 | let pong = ResponseType{ 107 | request: Request::new(node.clone(), u64::gen(64), RequestPayload::Ping), 108 | responder: node.clone(), 109 | payload: ResponsePayload::NoResult, 110 | }; 111 | let pong = RawMessage::new_p2p_response(pong); 112 | // check client heartbeats from client 113 | if Instant::now().duration_since(act.hb) > Duration::new(10, 0) { 114 | // heartbeat timed out 115 | println!("Client heartbeat failed, disconnecting!"); 116 | // stop actor 117 | ctx.stop(); 118 | } 119 | act.framed.write(pong); 120 | act.hb(ctx); 121 | }); 122 | } 123 | 124 | /// send a heartbeat message as client 125 | fn send_hb(&self, ctx: &mut Context) { 126 | let node = self.node.clone(); 127 | let ping = RequestType{ 128 | caller: self.node.clone(), 129 | request_id: u64::gen(64), 130 | payload: RequestPayload::Ping, 131 | }; 132 | 133 | ctx.run_later(Duration::new(1,0), move |act, ctx| { 134 | writeln!(io::stdout(), "client:{} send a ping", node.address.to_string()).unwrap(); 135 | let raw_message = RawMessage::new_p2p_request(ping); 136 | act.framed.write(raw_message); 137 | act.send_hb(ctx); 138 | }); 139 | } 140 | 141 | fn proccess_raw_msg(&self, msg: P2PMessage, ctx: &mut Context) { 142 | match msg { 143 | P2PMessage::Req(request) => { 144 | self.addr.do_send(request); 145 | }, 146 | P2PMessage::Resp(response) =>{ 147 | // TODO 148 | //self.addr.do_send(response); 149 | unimplemented!() 150 | }, 151 | } 152 | } 153 | } 154 | 155 | impl Handler for Session { 156 | type Result = (); 157 | fn handle(&mut self, msg: Message, _: &mut Context) { 158 | } 159 | } -------------------------------------------------------------------------------- /src/p2p/protocol.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::io::Cursor; 3 | use std::str::FromStr; 4 | 5 | use libp2p::{PeerId, Multiaddr}; 6 | use cryptocurrency_kit::crypto::{CryptoHash, Hash, hash}; 7 | use cryptocurrency_kit::storage::values::StorageValue; 8 | use serde::{Deserialize, Serialize}; 9 | 10 | #[derive(Debug, Clone, Deserialize, Serialize, Message, Eq, PartialEq)] 11 | pub enum P2PMsgCode { 12 | Ping, 13 | Handshake, 14 | Transaction, 15 | Block, 16 | Consensus, 17 | Sync, 18 | } 19 | 20 | implement_storagevalue_traits! {P2PMsgCode} 21 | implement_cryptohash_traits! {P2PMsgCode} 22 | 23 | #[derive(Debug, Clone, Copy)] 24 | pub enum BoundType { 25 | InBound, 26 | OutBound, 27 | } 28 | 29 | #[derive(Debug, Clone, Deserialize, Serialize, Message)] 30 | pub struct RawMessage { 31 | header: Header, 32 | payload: Payload, 33 | } 34 | 35 | implement_storagevalue_traits! {RawMessage} 36 | implement_cryptohash_traits! {RawMessage} 37 | 38 | impl RawMessage { 39 | pub fn new(header: Header, payload: Vec) -> Self { 40 | RawMessage { 41 | header: header, 42 | payload: payload, 43 | } 44 | } 45 | 46 | pub(crate) fn header(&self) -> &Header { 47 | &self.header 48 | } 49 | 50 | pub(crate) fn mut_header(&mut self) -> &mut Header { 51 | &mut self.header 52 | } 53 | 54 | pub(crate) fn payload(&self) -> &Payload { 55 | &self.payload 56 | } 57 | 58 | pub(crate) fn mut_payload(&mut self) -> &mut Payload { 59 | &mut self.payload 60 | } 61 | } 62 | 63 | #[derive(Debug, Clone, Deserialize, Serialize)] 64 | pub struct Header { 65 | pub code: P2PMsgCode, 66 | pub ttl: usize, 67 | // millis 68 | pub create_time: u64, 69 | pub peer_id: Option>, 70 | } 71 | 72 | implement_cryptohash_traits! {Header} 73 | implement_storagevalue_traits! {Header} 74 | 75 | impl Header { 76 | pub fn new(code: P2PMsgCode, ttl: usize, create_time: u64, peer_id: Option>) -> Self { 77 | Header { code: code, ttl: ttl, create_time: create_time, peer_id: peer_id } 78 | } 79 | } 80 | 81 | pub type Payload = Vec; 82 | 83 | #[derive(Debug, Clone, Deserialize, Serialize)] 84 | pub struct Handshake { 85 | version: String, 86 | peer_id: String, 87 | genesis: Hash, 88 | } 89 | 90 | implement_storagevalue_traits! {Handshake} 91 | implement_cryptohash_traits! {Handshake} 92 | 93 | impl Handshake { 94 | pub fn new(version: String, peer_id: PeerId, genesis: Hash) -> Self { 95 | let peer_id = peer_id.to_base58(); 96 | Handshake { 97 | version: version, 98 | peer_id: peer_id, 99 | genesis: genesis, 100 | } 101 | } 102 | 103 | pub fn version(&self) -> &String { 104 | &self.version 105 | } 106 | 107 | pub fn peer_id(&self) -> PeerId { 108 | PeerId::from_str(&self.peer_id).unwrap() 109 | } 110 | 111 | pub fn genesis(&self) -> &Hash { 112 | &self.genesis 113 | } 114 | } -------------------------------------------------------------------------------- /src/p2p/session.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::io; 3 | use std::net; 4 | use std::str::FromStr; 5 | use std::sync::mpsc::sync_channel; 6 | use std::time::Duration; 7 | 8 | use ::actix::prelude::*; 9 | use cryptocurrency_kit::storage::values::StorageValue; 10 | use futures::stream::once; 11 | use libp2p::multiaddr::Protocol; 12 | use libp2p::Multiaddr; 13 | use libp2p::PeerId; 14 | use cryptocurrency_kit::crypto::Hash; 15 | use tokio::{codec::FramedRead, io::WriteHalf, net::TcpListener, net::TcpStream}; 16 | 17 | use super::codec::MsgPacketCodec; 18 | use super::protocol::{BoundType, RawMessage, Header, P2PMsgCode, Handshake}; 19 | use super::server::{ServerEvent, SessionEvent, TcpServer}; 20 | use crate::common::multiaddr_to_ipv4; 21 | use crate::error::P2PError; 22 | 23 | pub struct Session { 24 | pid: Option>, 25 | peer_id: PeerId, 26 | local_id: PeerId, 27 | server: Addr, 28 | bound_type: BoundType, 29 | handshaked: bool, 30 | genesis: Hash, 31 | framed: actix::io::FramedWrite, MsgPacketCodec>, 32 | } 33 | 34 | impl Actor for Session { 35 | type Context = Context; 36 | 37 | fn started(&mut self, ctx: &mut Self::Context) { 38 | // send a handshake message 39 | { 40 | let peer_id = self.local_id.clone(); 41 | let handshake = Handshake::new("0.1.1".to_string(), peer_id.clone(), self.genesis.clone()); 42 | let raw_message = RawMessage::new( 43 | Header::new( 44 | P2PMsgCode::Handshake, 45 | 10, 46 | chrono::Local::now().timestamp_nanos() as u64, 47 | None, 48 | ), 49 | handshake.into_bytes(), 50 | ); 51 | ctx.add_message_stream(once(Ok(raw_message))); 52 | } 53 | 54 | ctx.run_later(Duration::from_secs(1), |act, ctx| { 55 | // pass 1s, not receive handshake packet, close the session 56 | if act.handshaked { 57 | return; 58 | } 59 | trace!( 60 | "Handshake timeout, {}, local_id: {}, peer: {}", 61 | act.handshaked, 62 | act.local_id.to_base58(), 63 | act.peer_id.to_base58() 64 | ); 65 | act.framed.close(); 66 | ctx.stop(); 67 | }); 68 | 69 | ctx.run_interval(Duration::from_secs(1), |act, ctx| { 70 | if act.handshaked { 71 | let raw_msg = RawMessage::new(Header::new( 72 | P2PMsgCode::Ping, 3, chrono::Local::now().timestamp_millis() as u64, None), 73 | vec![]); 74 | ctx.notify(raw_msg); 75 | } 76 | }); 77 | trace!("P2P session created"); 78 | } 79 | 80 | fn stopping(&mut self, _: &mut Self::Context) -> Running { 81 | if self.handshaked { 82 | self.server 83 | .do_send(ServerEvent::Disconnected(self.peer_id.clone())); 84 | self.framed.close(); 85 | } 86 | trace!( 87 | "P2P session stopped, handshake:{}, local_id: {}, peer: {}", 88 | self.handshaked, 89 | self.local_id.to_base58(), 90 | self.peer_id.to_base58() 91 | ); 92 | Running::Stop 93 | } 94 | } 95 | 96 | impl actix::io::WriteHandler for Session {} 97 | 98 | /// receive raw message from network, forward it to server 99 | impl StreamHandler for Session { 100 | fn handle(&mut self, msg: RawMessage, ctx: &mut Context) { 101 | debug!("Read message: {:?}, local_id:{:?}, peer_id:{:?}", msg.header(), self.local_id.to_base58(), self.peer_id.to_base58()); 102 | match msg.header().code { 103 | P2PMsgCode::Handshake => { 104 | self.server 105 | .send(ServerEvent::Connected( 106 | self.peer_id.clone(), 107 | self.bound_type, 108 | self.pid.as_ref().unwrap().clone(), 109 | msg.clone(), 110 | )) 111 | .into_actor(self) 112 | .then(|res, act, ctx| { 113 | match res { 114 | Ok(res) => { 115 | if let Err(err) = res { 116 | trace!("Author fail, err: {:?}", err); 117 | ctx.stop(); 118 | } else { 119 | let peer = res.unwrap(); 120 | act.handshaked = true; 121 | act.peer_id = peer; 122 | trace!( 123 | "Author successfully, local_id: {}, peer: {}", 124 | act.local_id.to_base58(), 125 | act.peer_id.to_base58() 126 | ); 127 | } 128 | } 129 | Err(err) => panic!(err), 130 | } 131 | actix::fut::ok(()) 132 | }) 133 | .wait(ctx); 134 | } 135 | P2PMsgCode::Transaction => {} 136 | P2PMsgCode::Block | P2PMsgCode::Consensus | P2PMsgCode::Sync => { 137 | self.server.do_send(ServerEvent::Message(self.peer_id.clone(), msg)); 138 | } 139 | P2PMsgCode::Ping => { 140 | assert!(self.handshaked); 141 | self.server 142 | .send(ServerEvent::Ping(self.peer_id.clone())) 143 | .into_actor(self) 144 | .then(|res, act, ctx| { 145 | match res { 146 | Ok(res) => { 147 | if res.is_err() { 148 | ctx.stop(); 149 | } 150 | } 151 | Err(err) => panic!(err), 152 | } 153 | actix::fut::ok(()) 154 | }) 155 | .wait(ctx); 156 | } 157 | _ => ctx.stop(), 158 | } 159 | } 160 | } 161 | 162 | /// receive raw message from server, forward it to network 163 | impl Handler for Session { 164 | type Result = (); 165 | 166 | fn handle(&mut self, msg: RawMessage, _: &mut Context) { 167 | if msg.header().code != P2PMsgCode::Ping { 168 | debug!("Write message: {:?}, local_id:{:?}, peer_id:{:?}", msg.header(), self.local_id.to_base58(), self.peer_id.to_base58()); 169 | } 170 | self.framed.write(msg); 171 | } 172 | } 173 | 174 | impl Handler for Session { 175 | type Result = (); 176 | fn handle(&mut self, msg: SessionEvent, ctx: &mut Context) { 177 | ctx.stop(); 178 | } 179 | } 180 | 181 | impl Session { 182 | pub fn new( 183 | self_pid: Addr, 184 | self_peer_id: PeerId, 185 | local_peer: PeerId, 186 | server: Addr, 187 | framed: actix::io::FramedWrite, MsgPacketCodec>, 188 | bound_type: BoundType, 189 | genesis: Hash, 190 | ) -> Session { 191 | Session { 192 | pid: Some(self_pid), 193 | peer_id: self_peer_id, 194 | local_id: local_peer, 195 | server: server, 196 | handshaked: false, 197 | framed: framed, 198 | bound_type: bound_type, 199 | genesis: genesis, 200 | } 201 | } 202 | } -------------------------------------------------------------------------------- /src/pprof/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | 3 | use ::actix::prelude::*; 4 | use futures::prelude::*; 5 | use tokio::prelude::*; 6 | use tokio_signal::unix::{Signal, SIGINT, SIGTERM}; 7 | 8 | pub fn spawn_signal_handler(dir: String) { 9 | let int_fut = Signal::new(SIGINT).flatten_stream(); 10 | let term_fut = Signal::new(SIGTERM).flatten_stream(); 11 | let s_stream = int_fut.select(term_fut); 12 | 13 | info!("Start signal handler"); 14 | flame::start("read file"); 15 | let code = System::run(move || { 16 | tokio::spawn( 17 | s_stream 18 | .into_future() 19 | .and_then(move |(item, _s)| { 20 | info!("Receive a signal, code: {}", item.unwrap()); 21 | System::current().stop(); 22 | flame::end("read file"); 23 | ::std::fs::create_dir_all(&dir).unwrap(); 24 | let graph = dir.to_owned() + "/flame-graph.html"; 25 | info!("flame graph=> {}", graph); 26 | flame::dump_html(&mut File::create(graph).unwrap()).unwrap(); 27 | future::ok(()) 28 | }) 29 | .map_err(|_err| ()), 30 | ); 31 | }); 32 | ::std::process::exit(code); 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | #[test] 40 | fn t_spawn_signal_handler() { 41 | use crate::common::random_dir; 42 | use crate::logger; 43 | logger::init_test_env_log(); 44 | spawn_signal_handler(*random_dir()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/store/entry.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::sync::Arc; 3 | 4 | use cryptocurrency_kit::crypto::{hash, CryptoHash, Hash}; 5 | use cryptocurrency_kit::storage::keys::StorageKey; 6 | use cryptocurrency_kit::storage::values::StorageValue; 7 | use cryptocurrency_kit::types::Zero; 8 | use kvdb_rocksdb::Database; 9 | 10 | use super::base_index::{BaseIndex, BaseIndexIter, IndexType}; 11 | 12 | #[derive(Debug)] 13 | pub struct Entry { 14 | base: BaseIndex, 15 | _v: PhantomData, 16 | } 17 | 18 | impl Entry 19 | where 20 | V: StorageValue, 21 | { 22 | pub fn new>(index_name: S, view: Arc) -> Self { 23 | Entry { 24 | base: BaseIndex::new(index_name, IndexType::Entry, view), 25 | _v: PhantomData, 26 | } 27 | } 28 | 29 | pub fn get(&self) -> Option { 30 | self.base.get(&Zero) 31 | } 32 | 33 | pub fn exists(&self) -> bool { 34 | self.base.contains(&Zero) 35 | } 36 | 37 | pub fn hash(&self) -> Hash { 38 | self.base 39 | .get::(&Zero) 40 | .map(|v| v.hash()) 41 | .unwrap_or_default() 42 | } 43 | ////////// 44 | pub fn set(&mut self, value: V) { 45 | self.base.put(&Zero, value) 46 | } 47 | 48 | pub fn remove(&mut self) { 49 | self.base.remove(&Zero) 50 | } 51 | 52 | pub fn take(&mut self) -> Option { 53 | let value: Option = self.get(); 54 | if value.is_some() { 55 | self.remove(); 56 | } 57 | value 58 | } 59 | 60 | pub fn swap(&mut self, value: V) -> Option { 61 | let pre_value = self.get(); 62 | self.set(value); 63 | pre_value 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use std::io::{self, Write}; 70 | 71 | use super::*; 72 | use crate::common::random_dir; 73 | use cryptocurrency_kit::crypto::EMPTY_HASH; 74 | 75 | #[test] 76 | fn entry() { 77 | let mut entry: Entry = Entry::new( 78 | "IDX_NAME", 79 | Arc::new(Database::open_default(&random_dir()).unwrap()), 80 | ); 81 | 82 | { 83 | assert_eq!(entry.get().is_none(), true); 84 | assert_eq!(entry.exists(), false); 85 | assert_eq!(entry.hash(), EMPTY_HASH); 86 | } 87 | 88 | { 89 | entry.set(10); 90 | assert_eq!(entry.get(), Some(10_i32)); 91 | assert_eq!(entry.exists(), true); 92 | writeln!(io::stdout(), "10_i32_entry => {:?}", entry.hash()).unwrap(); 93 | } 94 | 95 | { 96 | entry.remove(); 97 | assert_eq!(entry.get().is_none(), true); 98 | assert_eq!(entry.exists(), false); 99 | assert_eq!(entry.hash(), EMPTY_HASH); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/store/iter.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laohanlinux/consensus-rs/ebf0879ea3cf653c62ac79ff658c804a3bddcc6f/src/store/iter.rs -------------------------------------------------------------------------------- /src/store/list_index.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::{cell::Cell, marker::PhantomData}; 3 | 4 | use cryptocurrency_kit::crypto::*; 5 | use cryptocurrency_kit::storage::{keys::StorageKey, values::StorageValue}; 6 | use cryptocurrency_kit::types::Zero; 7 | use kvdb_rocksdb::{Database, DatabaseIterator}; 8 | 9 | use super::base_index::{BaseIndex, BaseIndexIter, IndexType}; 10 | 11 | /// data format 12 | /// |length|l-0, l-1, l-2, l-3| 13 | /// example: index_name: "IDX_NAME" 14 | /// length: IDX_NAME, 15 | /// list0: IDX_NAME0 16 | /// list1: IDX_NAME1, 17 | /// ListX: IDX_NAMEX, 18 | 19 | #[debug] 20 | pub struct ListIndex { 21 | base: BaseIndex, 22 | length: Cell>, 23 | _v: PhantomData, 24 | } 25 | 26 | #[debug] 27 | pub struct ListIndexIter<'a, V> { 28 | base_iter: BaseIndexIter<'a, u64, V>, 29 | } 30 | 31 | impl ListIndex 32 | where 33 | V: StorageValue, 34 | { 35 | pub fn new>(index_name: S, view: Arc) -> Self { 36 | Self { 37 | base: BaseIndex::new(index_name, IndexType::List, view), 38 | length: Cell::new(None), 39 | _v: PhantomData, 40 | } 41 | } 42 | 43 | pub fn get(&self, index: u64) -> Option 44 | where 45 | V: StorageValue, 46 | { 47 | self.base.get(&index) 48 | } 49 | 50 | pub fn last(&self) -> Option 51 | where 52 | V: StorageValue, 53 | { 54 | match self.len() { 55 | 0 => None, 56 | l => self.get(l - 1), 57 | } 58 | } 59 | 60 | pub fn is_empty(&self) -> bool { 61 | self.len() == 0 62 | } 63 | 64 | pub fn len(&self) -> u64 { 65 | if let Some(len) = self.length.get() { 66 | return len; 67 | } 68 | let len = self.base.get(&Zero).unwrap_or(0); 69 | self.length.set(Some(len)); 70 | len 71 | } 72 | 73 | pub fn iter(&self) -> ListIndexIter { 74 | ListIndexIter { 75 | base_iter: self.base.iter_from(&Zero, &0_u64), 76 | } 77 | } 78 | 79 | pub fn iter_from(&self, from: u64) -> ListIndexIter { 80 | ListIndexIter { 81 | base_iter: self.base.iter_from(&Zero, &from), 82 | } 83 | } 84 | 85 | /// mut 86 | pub fn set_len(&mut self, len: u64) { 87 | self.base.put(&Zero, len); 88 | self.length.set(Some(len)); 89 | } 90 | 91 | pub fn push(&mut self, value: V) { 92 | let len = self.len(); 93 | self.base.put(&len, value); 94 | self.set_len(len + 1) 95 | } 96 | 97 | pub fn pop(&mut self) -> Option { 98 | match self.len() { 99 | 0 => None, 100 | l => { 101 | let v = self.base.get(&(l - 1)); 102 | self.base.remove(&(l - 1)); 103 | self.set_len(l - 1); 104 | v 105 | } 106 | } 107 | } 108 | 109 | pub fn extend(&mut self, iter: I) 110 | where 111 | I: IntoIterator, 112 | { 113 | use std::io::{self, Write}; 114 | let mut len = self.len(); 115 | for value in iter { 116 | self.base.put(&len, value); 117 | len += 1; 118 | } 119 | self.base.put(&Zero, len); 120 | self.set_len(len); 121 | } 122 | 123 | pub fn truncate(&mut self, len: u64) { 124 | while self.len() > len { 125 | self.pop(); 126 | } 127 | } 128 | 129 | pub fn set(&mut self, index: u64, value: V) { 130 | if index >= self.len() { 131 | panic!( 132 | "index out of bound: \ 133 | the len is {} but the index is {}", 134 | self.len(), 135 | index, 136 | ); 137 | } 138 | self.base.put(&index, value); 139 | } 140 | 141 | pub fn clear(&mut self) { 142 | self.length.set(Some(0)); 143 | self.base.clear(); 144 | } 145 | } 146 | 147 | impl<'a, V> ::std::iter::IntoIterator for &'a ListIndex 148 | where 149 | V: StorageValue, 150 | { 151 | type Item = V; 152 | type IntoIter = ListIndexIter<'a, V>; 153 | 154 | fn into_iter(self) -> Self::IntoIter { 155 | self.iter() 156 | } 157 | } 158 | 159 | impl<'a, V> Iterator for ListIndexIter<'a, V> 160 | where 161 | V: StorageValue, 162 | { 163 | type Item = V; 164 | fn next(&mut self) -> Option { 165 | self.base_iter.next().map(|(.., v)| v) 166 | } 167 | } 168 | 169 | #[cfg(test)] 170 | mod tests { 171 | use super::*; 172 | use std::io::{self, Write}; 173 | 174 | fn list_index_methods(list_index: &mut ListIndex) { 175 | assert!(list_index.is_empty()); 176 | assert_eq!(0, list_index.len()); 177 | assert!(list_index.last().is_none()); 178 | assert_eq!(None, list_index.pop()); 179 | 180 | let extended_by = vec![45, 3422, 234]; 181 | list_index.extend(extended_by); 182 | assert!(!list_index.is_empty()); 183 | assert_eq!(Some(45), list_index.get(0)); 184 | assert_eq!(Some(3422), list_index.get(1)); 185 | assert_eq!(Some(234), list_index.get(2)); 186 | assert_eq!(3, list_index.len()); 187 | 188 | list_index.set(2, 777); 189 | assert_eq!(Some(777), list_index.get(2)); 190 | assert_eq!(Some(777), list_index.last()); 191 | assert_eq!(3, list_index.len()); 192 | 193 | let mut extended_by_again = vec![666, 999]; 194 | for el in &extended_by_again { 195 | list_index.push(*el); 196 | } 197 | assert_eq!(Some(666), list_index.get(3)); 198 | assert_eq!(Some(999), list_index.get(4)); 199 | assert_eq!(5, list_index.len()); 200 | extended_by_again[1] = 1001; 201 | list_index.extend(extended_by_again); 202 | assert_eq!(7, list_index.len()); 203 | assert_eq!(Some(1001), list_index.last()); 204 | 205 | assert_eq!(Some(1001), list_index.pop()); 206 | assert_eq!(6, list_index.len()); 207 | 208 | list_index.truncate(3); 209 | 210 | assert_eq!(3, list_index.len()); 211 | assert_eq!(Some(777), list_index.last()); 212 | 213 | list_index.clear(); 214 | assert_eq!(0, list_index.len()); 215 | } 216 | 217 | fn list_index_iter(list_index: &mut ListIndex) { 218 | 219 | list_index.extend(vec![1u8, 2, 3]); 220 | 221 | assert_eq!(list_index.len(), 3); 222 | assert_eq!(list_index.get(0).unwrap(), 1); 223 | assert_eq!(list_index.get(1).unwrap(), 2); 224 | assert_eq!(list_index.get(2).unwrap(), 3); 225 | assert_eq!(list_index.last().unwrap(), 3); 226 | assert_eq!(list_index.iter().collect::>(), vec![1, 2, 3]); 227 | assert_eq!(list_index.iter_from(0).collect::>(), vec![1, 2, 3]); 228 | assert_eq!(list_index.iter_from(1).collect::>(), vec![2, 3]); 229 | assert_eq!( 230 | list_index.iter_from(3).collect::>(), 231 | Vec::::new() 232 | ); 233 | } 234 | fn newdb() -> Database { 235 | use crate::common::random_dir; 236 | Database::open_default(&random_dir()).unwrap() 237 | } 238 | mod rocksdb_tests { 239 | use super::*; 240 | const IDX_NAME: &'static str = "idx_name"; 241 | 242 | #[test] 243 | fn test_list_index_methods() { 244 | let db = Arc::new(newdb()); 245 | let mut list_index = ListIndex::new(IDX_NAME, db.clone()); 246 | super::list_index_methods(&mut list_index); 247 | } 248 | 249 | #[test] 250 | fn test_list_index_iter(){ 251 | let db = Arc::new(newdb()); 252 | let mut list_index = ListIndex::new(IDX_NAME, db.clone()); 253 | super::list_index_iter(&mut list_index); 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/store/map_index.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::{borrow::Borrow, marker::PhantomData}; 3 | 4 | use cryptocurrency_kit::storage::{keys::StorageKey, values::StorageValue}; 5 | use cryptocurrency_kit::types::Zero; 6 | use kvdb_rocksdb::Database; 7 | 8 | use super::base_index::{BaseIndex, BaseIndexIter, IndexType}; 9 | 10 | //#[derive(Debug)] 11 | pub struct MapIndex { 12 | base: BaseIndex, 13 | _k: PhantomData, 14 | _v: PhantomData, 15 | } 16 | 17 | pub struct MapIndexIter<'a, K, V> { 18 | base_iter: BaseIndexIter<'a, K, V>, 19 | } 20 | 21 | impl<'a, K, V> Iterator for MapIndexIter<'a, K, V> 22 | where 23 | K: StorageKey, 24 | V: StorageValue, 25 | { 26 | type Item = (K::Owned, V); 27 | 28 | fn next(&mut self) -> Option { 29 | self.base_iter.next() 30 | } 31 | } 32 | 33 | //#[derive(Debug, Clone)] 34 | pub struct MapIndexKeys<'a, K> { 35 | base_iter: BaseIndexIter<'a, K, Zero>, 36 | } 37 | 38 | pub struct MapIndexValues<'a, V> { 39 | base_iter: BaseIndexIter<'a, Zero, V>, 40 | } 41 | 42 | impl MapIndex 43 | where 44 | K: StorageKey, 45 | V: StorageValue, 46 | { 47 | pub fn new>(index_name: S, view: Arc) -> Self { 48 | Self { 49 | base: BaseIndex::new(index_name, IndexType::Map, view), 50 | _k: PhantomData, 51 | _v: PhantomData, 52 | } 53 | } 54 | 55 | // pub fn new_in_family, I: StorageKey>( 56 | // familly_name: S, 57 | // index_id: &T, 58 | // view: T, 59 | // ) -> Self { 60 | // Self { 61 | // 62 | // } 63 | // } 64 | 65 | pub fn get(&self, key: &Q) -> Option 66 | where 67 | K: Borrow, 68 | Q: StorageKey + ?Sized, 69 | { 70 | self.base.get(key) 71 | } 72 | 73 | pub fn contains(&self, key: &Q) -> bool 74 | where 75 | K: Borrow, 76 | Q: StorageKey + ?Sized, 77 | { 78 | self.base.contains(key) 79 | } 80 | 81 | pub fn iter(&self) -> MapIndexIter { 82 | MapIndexIter { 83 | base_iter: self.base.iter(&()), 84 | } 85 | } 86 | 87 | pub fn keys(&self) -> MapIndexKeys { 88 | MapIndexKeys { 89 | base_iter: self.base.iter(&()), 90 | } 91 | } 92 | 93 | pub fn values(&self) -> MapIndexValues { 94 | MapIndexValues { 95 | base_iter: self.base.iter(&()), 96 | } 97 | } 98 | } 99 | 100 | impl MapIndex 101 | where 102 | K: StorageKey, 103 | V: StorageValue, 104 | { 105 | pub fn put(&mut self, key: &K, value: V) { 106 | self.base.put(key, value) 107 | } 108 | 109 | pub fn remove(&mut self, key: &Q) 110 | where 111 | K: Borrow, 112 | Q: StorageKey + ?Sized, 113 | { 114 | self.base.remove(key) 115 | } 116 | 117 | pub fn clear(&mut self) { 118 | self.base.clear() 119 | } 120 | } 121 | 122 | impl<'a, K> Iterator for MapIndexKeys<'a, K> 123 | where 124 | K: StorageKey, 125 | { 126 | type Item = K::Owned; 127 | 128 | fn next(&mut self) -> Option { 129 | // ignore the value 130 | self.base_iter.next().map(|(k, ..)| k) 131 | } 132 | } 133 | 134 | impl<'a, V> Iterator for MapIndexValues<'a, V> 135 | where 136 | V: StorageValue, 137 | { 138 | type Item = V; 139 | 140 | fn next(&mut self) -> Option { 141 | self.base_iter.next().map(|(.., v)| v) 142 | } 143 | } 144 | 145 | #[cfg(test)] 146 | mod tests { 147 | use super::*; 148 | use crate::common::random_dir; 149 | use std::io::{self, Write}; 150 | 151 | const IDX_NAME: &'static str = "idx_name_"; 152 | 153 | fn newdb() -> Database { 154 | Database::open_default(&random_dir()).unwrap() 155 | } 156 | 157 | #[test] 158 | fn str_key() { 159 | let db = Arc::new(newdb()); 160 | const KEY: &str = "key_1"; 161 | let mut index: MapIndex = MapIndex::new(IDX_NAME, db); 162 | assert_eq!(false, index.contains(KEY)); 163 | index.put(&KEY.to_owned(), 0); 164 | assert_eq!(true, index.contains(KEY)); 165 | } 166 | 167 | #[test] 168 | fn key_iter() { 169 | let db = Arc::new(newdb()); 170 | let mut index: MapIndex = MapIndex::new(IDX_NAME, db.clone()); 171 | let keys = index.keys(); 172 | assert_eq!(keys.count(), 0); 173 | 174 | (0..100).for_each(|idx| { 175 | index.put(&format!("{}", idx), (idx + 1).to_string()); 176 | }); 177 | let ref mut keys = index.keys(); 178 | assert_eq!(keys.count(), 100); 179 | 180 | let ref mut keys = index.keys(); 181 | keys.for_each(|key| { 182 | writeln!(io::stdout(), "key: {}", key).unwrap(); 183 | }); 184 | } 185 | 186 | #[test] 187 | fn value_iter() { 188 | let db = Arc::new(newdb()); 189 | let mut index: MapIndex = MapIndex::new(IDX_NAME, db.clone()); 190 | assert_eq!(index.values().count(), 0); 191 | 192 | (0..100).for_each(|idx| { 193 | index.put(&format!("{}", idx), (idx + 1).to_string()); 194 | }); 195 | assert_eq!(index.values().count(), 100); 196 | 197 | let ref mut values = index.values(); 198 | values.for_each(|value| { 199 | writeln!(io::stdout(), "value: {}", value).unwrap(); 200 | }); 201 | } 202 | 203 | #[test] 204 | fn map_iter() { 205 | let db = Arc::new(newdb()); 206 | 207 | { 208 | let mut index: MapIndex = MapIndex::new(IDX_NAME, db.clone()); 209 | 210 | (0..100).for_each(|idx| { 211 | index.put(&format!("{}", idx), idx + 1); 212 | }); 213 | let keys = index.keys(); 214 | assert_eq!(keys.count(), 100); 215 | } 216 | 217 | { 218 | let mut index: MapIndex = 219 | MapIndex::new("index2_name".to_string(), db.clone()); 220 | (0..100).for_each(|idx| { 221 | index.put(&format!("{}", idx), idx + 1); 222 | }); 223 | 224 | let iter = index.iter(); 225 | iter.for_each(|(key, value)| { 226 | writeln!(io::stdout(), "key: {}, value: {}", key, value).unwrap(); 227 | }); 228 | 229 | let iter = index.iter(); 230 | assert_eq!(iter.count(), 100); 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/store/mod.rs: -------------------------------------------------------------------------------- 1 | mod base_index; 2 | mod entry; 3 | mod list_index; 4 | mod map_index; 5 | mod iter; 6 | pub mod schema; 7 | mod types; -------------------------------------------------------------------------------- /src/store/schema.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use cryptocurrency_kit::crypto::{hash, CryptoHash, Hash}; 4 | use cryptocurrency_kit::storage::values::StorageValue; 5 | use cryptocurrency_kit::ethkey::Address; 6 | use kvdb_rocksdb::Database; 7 | 8 | use super::entry::Entry; 9 | use super::list_index::ListIndex; 10 | use super::map_index::MapIndex; 11 | use crate::{ 12 | types::block::{Block, Header}, 13 | types::{Validator, ValidatorArray, HashesEntry, Bloom, Height, transaction::Transaction}, 14 | }; 15 | 16 | macro_rules! define_name { 17 | ( 18 | $( 19 | $name:ident => $value:expr; 20 | )+ 21 | ) => ( 22 | $(const $name: &str = concat!("core.", $value);)* 23 | ); 24 | } 25 | 26 | define_name!( 27 | TRANSACTIONS => "transaction_hash"; 28 | TRANSACTIONS_HASH => "transaction_block"; 29 | BLOCKS => "blocks"; 30 | HEADERS => "headers"; 31 | BLOCK_HASHES_BY_HEIGHT => "block_hashes_by_height"; 32 | BLOCK_TRANSACTIONS => "block_transactions"; 33 | PRECOMMITS => "precommits"; 34 | CONFIGS => "configs"; 35 | CONSENSUS_MESSAGE_CACHE => "consensus_message_cache"; 36 | VALIDATORS => "validators"; 37 | ); 38 | 39 | struct TxLocation { 40 | block_height: Height, 41 | position_in_block: u64, 42 | } 43 | 44 | pub struct Schema { 45 | db: Arc, 46 | } 47 | 48 | impl Schema { 49 | pub fn new(db: Arc) -> Self { 50 | Schema { db } 51 | } 52 | 53 | pub fn transaction(&self) -> MapIndex { 54 | MapIndex::new(TRANSACTIONS, self.db.clone()) 55 | } 56 | 57 | pub fn transaction_hashes(&self) -> MapIndex { 58 | MapIndex::new(TRANSACTIONS_HASH, self.db.clone()) 59 | } 60 | 61 | pub fn blocks(&self) -> MapIndex { 62 | MapIndex::new(BLOCKS, self.db.clone()) 63 | } 64 | 65 | pub fn headers(&self) -> MapIndex { 66 | MapIndex::new(HEADERS, self.db.clone()) 67 | } 68 | 69 | pub fn block_hashes_by_height(&self) -> ListIndex { 70 | ListIndex::new(BLOCK_HASHES_BY_HEIGHT, self.db.clone()) 71 | } 72 | 73 | pub fn block_hash_by_height(&self, height: Height) -> Option { 74 | self.block_hashes_by_height().get(height) 75 | } 76 | 77 | pub fn last_block(&self) -> Block { 78 | let hash = self.block_hashes_by_height() 79 | .last() 80 | .expect("An attempt to get the `last_block` during creating the genesis block ."); 81 | let header = self.headers().get(&hash).unwrap(); 82 | Block::new2(header, vec![]) 83 | } 84 | 85 | pub fn validators(&self) -> Entry { 86 | Entry::new(VALIDATORS, self.db.clone()) 87 | } 88 | 89 | /// Returns the height of the last committed block. 90 | /// 91 | /// #Panic 92 | /// 93 | /// Panic if the "genesis block" was not created 94 | /// 95 | /// (len - 1) because including a genesis hash 96 | pub fn height(&self) -> Height { 97 | let len = self.block_hashes_by_height().len(); 98 | assert!( 99 | len > 0, 100 | "An attempt to get the actual `height` during creating the genesis block." 101 | ); 102 | len - 1 103 | } 104 | } 105 | 106 | #[cfg(test)] 107 | mod tests { 108 | use super::*; 109 | use crate::common::random_dir; 110 | use cryptocurrency_kit::ethkey::{Generator, KeyPair, Random, Secret}; 111 | use std::io::{self, Write}; 112 | use std::sync::Arc; 113 | 114 | fn random_secret() -> Secret { 115 | let key_pair = Random.generate().unwrap(); 116 | key_pair.secret().clone() 117 | } 118 | 119 | #[test] 120 | fn tschema() { 121 | let db = Arc::new(Database::open_default(&random_dir()).unwrap()); 122 | let schema = Schema::new(db.clone()); 123 | 124 | /// block_hashes_by_height 125 | { 126 | let mut ledger = schema.block_hashes_by_height(); 127 | (0..100).for_each(|idx| { 128 | ledger.push(idx.hash()); 129 | }); 130 | let iter = ledger.iter(); 131 | iter.for_each(|hash| { 132 | writeln!(io::stdout(), "{}", hash.short()).unwrap(); 133 | }); 134 | writeln!(io::stdout(), "-->{:?}", ledger.get(1).unwrap()).unwrap(); 135 | } 136 | 137 | // transaction 138 | { 139 | let mut tx = schema.transaction(); 140 | let mut zero_tx = Transaction::new( 141 | 1024, 142 | ::cryptocurrency_kit::ethkey::Address::from(100), 143 | 0, 144 | 0, 145 | 0, 146 | vec![1, 2, 3], 147 | ); 148 | zero_tx.sign(1, &random_secret()); 149 | writeln!( 150 | io::stdout(), 151 | "transaction hash ===> {:#?}", 152 | zero_tx.hash().short() 153 | ) 154 | .unwrap(); 155 | { 156 | let buf = zero_tx.clone().into_bytes(); 157 | let _zero_tx1 = Transaction::from_bytes(::std::borrow::Cow::from(buf)); 158 | } 159 | tx.put(&zero_tx.hash(), zero_tx.clone()); 160 | let zero_tx1 = schema.transaction().get(&zero_tx.hash()); 161 | assert_eq!(zero_tx1.is_some(), true); 162 | writeln!(io::stdout(), "{:#?}", zero_tx1.unwrap()).unwrap(); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/store/types.rs: -------------------------------------------------------------------------------- 1 | use kvdb::DBTransaction; 2 | use kvdb_rocksdb::DatabaseIterator; 3 | 4 | pub type Iter<'a> = DatabaseIterator<'a>; 5 | 6 | 7 | pub trait Snapshot: 'static { 8 | fn get(&self, name: &str, key: &[u8]) -> Option>; 9 | 10 | fn contains(&self, name: &str, key: &[u8]) -> bool { 11 | self.get(name, key).is_some() 12 | } 13 | 14 | fn iter<'a> (&'a self, name: &str, from: &[u8]) -> Iter<'a>; 15 | } 16 | 17 | pub struct Fork { 18 | snapshot: Box, 19 | transaction: DBTransaction, 20 | } 21 | 22 | // 23 | //impl Snapshot for Fork { 24 | // fn get(&self, name: &str, key: &[u8]) -> Option> { 25 | // 26 | // } 27 | //} -------------------------------------------------------------------------------- /src/subscriber/events.rs: -------------------------------------------------------------------------------- 1 | use ::actix::prelude::*; 2 | use actix_broker::BrokerIssue; 3 | use libp2p::PeerId; 4 | 5 | use crate::types::block::{Header, Block, Blocks}; 6 | use crate::types::Height; 7 | 8 | pub const MAX_MAILBOX_CAPACITY: usize = 1 << 11; 9 | 10 | #[derive(Message, Clone, Debug)] 11 | pub enum ChainEvent { 12 | NewBlock(Block), 13 | NewHeader(Header), 14 | SyncBlock(Height), 15 | PostBlock(Option, Blocks), 16 | } 17 | 18 | // cross thread event 19 | pub mod ChainEventCT { 20 | use ::actix::prelude::*; 21 | use super::ChainEvent; 22 | use crate::subscriber::impl_subscribe_handler; 23 | 24 | impl_subscribe_handler! {ChainEvent} 25 | } 26 | 27 | pub enum SubscriberType { 28 | Async, 29 | Sync, 30 | } 31 | 32 | pub struct ChainEventSubscriber { 33 | subscriber_type: SubscriberType, 34 | } 35 | 36 | impl Actor for ChainEventSubscriber { 37 | type Context = Context; 38 | 39 | fn started(&mut self, _ctx: &mut Self::Context) { 40 | info!("Chain event subscriber has started"); 41 | } 42 | 43 | fn stopped(&mut self, _ctx: &mut Self::Context) { 44 | info!("Chain event subscriber has stopped"); 45 | } 46 | } 47 | 48 | impl Handler for ChainEventSubscriber { 49 | type Result = (); 50 | 51 | fn handle(&mut self, msg: ChainEvent, ctx: &mut Self::Context) -> Self::Result { 52 | match self.subscriber_type { 53 | SubscriberType::Async => { 54 | self.issue_async(msg); 55 | } 56 | SubscriberType::Sync => { 57 | self.issue_sync(msg, ctx); 58 | } 59 | } 60 | } 61 | } 62 | 63 | impl ChainEventSubscriber { 64 | pub fn new(subscriber_type: SubscriberType) -> Self { 65 | ChainEventSubscriber { 66 | subscriber_type: subscriber_type, 67 | } 68 | } 69 | } 70 | 71 | 72 | use crate::types::transaction::Transaction; 73 | use crate::protocol::GossipMessage; 74 | 75 | #[derive(Message, Clone, Debug)] 76 | pub enum BroadcastEvent { 77 | Transaction(Transaction), 78 | Blocks(Option, Blocks), 79 | Consensus(GossipMessage), 80 | Sync(Height), 81 | } 82 | 83 | pub struct BroadcastEventSubscriber { 84 | subscriber_type: SubscriberType, 85 | } 86 | 87 | impl Actor for BroadcastEventSubscriber { 88 | type Context = Context; 89 | 90 | fn started(&mut self, ctx: &mut Self::Context) { 91 | ctx.set_mailbox_capacity(MAX_MAILBOX_CAPACITY); 92 | info!("Broadcast event subscriber has started"); 93 | } 94 | 95 | fn stopped(&mut self, _ctx: &mut Self::Context) { 96 | info!("Broadcast event subscriber has stopped"); 97 | } 98 | } 99 | 100 | impl Handler for BroadcastEventSubscriber { 101 | type Result = (); 102 | 103 | fn handle(&mut self, msg: BroadcastEvent, ctx: &mut Self::Context) -> Self::Result { 104 | debug!("BroadcastEventSubscriber[e:BroadcastEvent]"); 105 | match self.subscriber_type { 106 | SubscriberType::Async => { 107 | self.issue_async(msg); 108 | } 109 | SubscriberType::Sync => { 110 | self.issue_sync(msg, ctx); 111 | } 112 | } 113 | } 114 | } 115 | 116 | impl BroadcastEventSubscriber { 117 | pub fn new(subscriber_type: SubscriberType) -> Self { 118 | BroadcastEventSubscriber { 119 | subscriber_type: subscriber_type, 120 | } 121 | } 122 | } 123 | 124 | #[cfg(test)] 125 | mod test { 126 | use super::*; 127 | use actix_broker::BrokerSubscribe; 128 | use actix_broker::Broker; 129 | 130 | struct ProActor { 131 | name: String, 132 | } 133 | 134 | impl Actor for ProActor { 135 | type Context = Context; 136 | 137 | fn started(&mut self, ctx: &mut Self::Context) { 138 | self.subscribe_async::(ctx); 139 | } 140 | } 141 | 142 | impl Handler for ProActor { 143 | type Result = (); 144 | 145 | fn handle(&mut self, msg: BroadcastEvent, _ctx: &mut Self::Context) { 146 | println!("ProActor[{}] Received: {:?}", self.name, msg); 147 | } 148 | } 149 | 150 | struct SubActor {} 151 | 152 | impl Actor for SubActor { 153 | type Context = Context; 154 | 155 | fn started(&mut self, ctx: &mut Self::Context) {} 156 | } 157 | 158 | impl Handler for SubActor { 159 | type Result = (); 160 | 161 | fn handle(&mut self, msg: BroadcastEvent, _ctx: &mut Self::Context) { 162 | println!("SubActor Received: {:?}", msg); 163 | // self.issue_async(msg); 164 | Broker::issue_async(msg); 165 | } 166 | } 167 | 168 | #[test] 169 | fn t_async_actor() { 170 | use crate::protocol::{GossipMessage, MessageType}; 171 | crate::logger::init_test_env_log(); 172 | let pro = ProActor { name: "Same thread".to_owned() }.start(); 173 | 174 | ::std::thread::spawn(move || { 175 | System::run(move || { 176 | let pro = ProActor { name: "Cross thread".to_owned() }.start(); 177 | }); 178 | }); 179 | 180 | // customer 181 | // let sub = BroadcastEventSubscriber { subscriber_type: SubscriberType::Async }.start(); 182 | let sub = SubActor {}.start(); 183 | 184 | ::std::thread::spawn(move || { 185 | while true { 186 | sub.do_send(BroadcastEvent::Consensus(GossipMessage::new(MessageType::RoundChange, vec![], None))); 187 | ::std::thread::sleep(::std::time::Duration::from_secs(2)); 188 | } 189 | }); 190 | 191 | crate::pprof::spawn_signal_handler(*crate::common::random_dir()); 192 | } 193 | } -------------------------------------------------------------------------------- /src/subscriber/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | use ::actix::prelude::*; 3 | use libp2p::Multiaddr; 4 | use libp2p::PeerId; 5 | 6 | pub mod events; 7 | 8 | use crate::types::block::{Block, Header}; 9 | use super::*; 10 | 11 | #[derive(Message, Clone, Debug)] 12 | pub enum P2PEvent { 13 | AddPeer(PeerId, Vec), 14 | DropPeer(PeerId, Vec), 15 | } 16 | 17 | impl_subscribe_handler! {P2PEvent} 18 | 19 | pub fn spawn_sync_subscriber() -> Addr { 20 | Actor::create(|_| ProcessSignals { 21 | subscribers: vec![], 22 | }) 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! impl_subscribe_handler { 27 | ($key: ident) => { 28 | #[derive(Message)] 29 | pub enum SubscribeMessage { 30 | SubScribe(Recipient<$key>), 31 | UnSubScribe(Recipient<$key>), 32 | } 33 | 34 | impl SubscribeMessage { 35 | pub fn new_subscribe(recipient: Recipient<$key>) -> Self { 36 | SubscribeMessage::SubScribe(recipient) 37 | } 38 | 39 | pub fn new_unsubscribe(recipient: Recipient<$key>) -> Self { 40 | SubscribeMessage::UnSubScribe(recipient) 41 | } 42 | } 43 | 44 | #[derive(Clone)] 45 | pub struct ProcessSignals { 46 | subscribers: Vec>, 47 | } 48 | 49 | impl Actor for ProcessSignals { 50 | type Context = Context; 51 | } 52 | 53 | impl Handler<$key> for ProcessSignals { 54 | type Result = (); 55 | fn handle(&mut self, msg: $key, _: &mut Self::Context) { 56 | trace!("Receive a notify message"); 57 | self.distribute(msg); 58 | } 59 | } 60 | 61 | impl Handler for ProcessSignals { 62 | type Result = (); 63 | 64 | fn handle(&mut self, msg: SubscribeMessage, _: &mut Self::Context) { 65 | trace!("Receive a subscibe message"); 66 | match msg { 67 | SubscribeMessage::SubScribe(recipient) => { 68 | self.subscribe(recipient); 69 | } 70 | SubscribeMessage::UnSubScribe(recipient) => { 71 | self.unsubscribe(recipient); 72 | } 73 | } 74 | } 75 | } 76 | 77 | impl ProcessSignals { 78 | pub fn new() -> Self { 79 | ProcessSignals {subscribers: vec![]} 80 | } 81 | 82 | pub fn subscribe(&mut self, recipient: Recipient<$key>) { 83 | self.subscribers.push(recipient); 84 | } 85 | 86 | pub fn unsubscribe(&mut self, recipient: Recipient<$key>) { 87 | self.subscribers.remove_item(&recipient); 88 | } 89 | 90 | /// Async send a message to subscriber mailbox 91 | pub fn distribute(&mut self, msg: $key) { 92 | for subscriber in &self.subscribers { 93 | subscriber.do_send(msg.clone()); 94 | } 95 | } 96 | } 97 | }; 98 | } 99 | 100 | #[cfg(test)] 101 | mod tests { 102 | use futures::future; 103 | use futures::Future; 104 | use super::*; 105 | use std::io::{self, Write}; 106 | 107 | struct Worker { 108 | name: String, 109 | } 110 | 111 | impl Actor for Worker { 112 | type Context = Context; 113 | } 114 | 115 | #[derive(Message, Debug, Clone)] 116 | pub struct RawMessage { 117 | tm: std::time::Duration, 118 | } 119 | 120 | impl Handler for Worker { 121 | type Result = (); 122 | fn handle(&mut self, msg: RawMessage, _: &mut Self::Context) { 123 | use std::io::{self, Write}; 124 | writeln!(io::stdout(), "[{}] worker receive a msg: {:?}", self.name, msg).unwrap(); 125 | } 126 | } 127 | 128 | impl_subscribe_handler! {RawMessage} 129 | 130 | #[test] 131 | fn t_subscribe() { 132 | use chrono::Local; 133 | use chrono::Timelike; 134 | let system = System::new("test"); 135 | let subscribe_pid = Actor::create(|_| ProcessSignals { 136 | subscribers: vec![], 137 | }); 138 | (0..10).for_each(|_idx| { 139 | let name = format!("{}", Local::now().time().nanosecond()); 140 | let worker = Worker::create(|_| Worker { 141 | name: name, 142 | }); 143 | let recipient = worker.recipient(); 144 | let message = SubscribeMessage::SubScribe(recipient); 145 | let request = subscribe_pid.clone().send(message); 146 | Arbiter::spawn(request.then(|_| { 147 | future::result(Ok(())) 148 | })); 149 | }); 150 | 151 | (0..230).for_each(|idx| { 152 | subscribe_pid.do_send(RawMessage { 153 | tm: std::time::Duration::from_secs(idx), 154 | }); 155 | }); 156 | Arbiter::spawn_fn(|| { 157 | System::current().stop(); 158 | future::ok(()) 159 | }); 160 | system.run(); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/types/block.rs: -------------------------------------------------------------------------------- 1 | use cryptocurrency_kit::crypto::{hash, CryptoHash, Hash, EMPTY_HASH}; 2 | use cryptocurrency_kit::storage::values::StorageValue; 3 | use cryptocurrency_kit::ethkey::signature::*; 4 | use cryptocurrency_kit::ethkey::{Address, Secret, Signature}; 5 | use serde::{Deserialize, Serialize}; 6 | use serde_json::to_string; 7 | 8 | use std::io::Cursor; 9 | use std::borrow::Cow; 10 | 11 | use super::transaction::Transaction; 12 | use super::votes::Votes; 13 | use super::{Bloom, Difficulty, Gas, Height, Timestamp}; 14 | 15 | #[derive(Serialize, Deserialize, Debug, Clone)] 16 | pub struct Header { 17 | pub prev_hash: Hash, 18 | pub proposer: Address, 19 | pub root: Hash, 20 | // state root 21 | pub tx_hash: Hash, 22 | // transactions root 23 | pub receipt_hash: Hash, 24 | // receipt_root 25 | pub bloom: Bloom, 26 | pub difficulty: Difficulty, 27 | pub height: Height, 28 | pub gas_limit: Gas, 29 | pub gas_used: Gas, 30 | pub time: Timestamp, 31 | #[serde(default)] 32 | pub extra: Option>, 33 | #[serde(default)] 34 | pub votes: Option, 35 | #[serde(skip_serializing, skip_deserializing)] 36 | hash_cache: Option, // use atomic pre instant of it 37 | } 38 | 39 | implement_cryptohash_traits! {Header} 40 | implement_storagevalue_traits! {Header} 41 | 42 | impl Header { 43 | pub fn new( 44 | prev_hash: Hash, 45 | proposer: Address, 46 | root: Hash, 47 | tx_hash: Hash, 48 | receipt_hash: Hash, 49 | bloom: Bloom, 50 | diff: Difficulty, 51 | height: Height, 52 | gas_limit: Gas, 53 | gas_used: Gas, 54 | tm: Timestamp, 55 | votes: Option, 56 | extra: Option>, 57 | ) -> Self { 58 | Header { 59 | prev_hash, 60 | proposer, 61 | root, 62 | tx_hash, 63 | receipt_hash, 64 | bloom, 65 | difficulty: diff, 66 | height, 67 | gas_limit, 68 | gas_used, 69 | time: tm, 70 | extra, 71 | votes, 72 | hash_cache: None, 73 | } 74 | } 75 | 76 | pub fn block_hash(&self) -> Hash { 77 | self.hash_cache.map_or_else(|| { 78 | let mut header = self.clone(); 79 | header.votes = None; 80 |
::hash(&header) 81 | }, |hash| hash) 82 | } 83 | 84 | pub fn new_mock(pre_hash: Hash, proposer: Address, tx_hash: Hash, height: Height, tm: Timestamp, extra: Option>) -> Self { 85 | Self::new(pre_hash, proposer, EMPTY_HASH, tx_hash, EMPTY_HASH, 0, 0, height, 0, 0, tm, None, extra) 86 | } 87 | 88 | pub fn cache_hash(&mut self, block_hash: Option) { 89 | if let Some(block_hash) = block_hash { 90 | self.hash_cache = Some(block_hash); 91 | } else { 92 | let block_hash = self.block_hash(); 93 | self.hash_cache = Some(block_hash); 94 | } 95 | } 96 | 97 | pub fn zero_header() -> Header { 98 | Header { 99 | prev_hash: Hash::zero(), 100 | proposer: Address::from(0), 101 | root: Hash::zero(), 102 | tx_hash: Hash::zero(), 103 | receipt_hash: Hash::zero(), 104 | bloom: 0, 105 | difficulty: 0, 106 | height: 0, 107 | gas_limit: 0, 108 | gas_used: 0, 109 | time: 0, 110 | extra: None, 111 | votes: None, 112 | hash_cache: None, 113 | } 114 | } 115 | } 116 | 117 | #[derive(Serialize, Deserialize, Debug, Clone)] 118 | struct HeaderBytes<'a> { 119 | pub prev_hash: Cow<'a, Hash>, 120 | pub proposer: Address, 121 | pub root: Cow<'a, Hash>, 122 | // state root 123 | pub tx_hash: Cow<'a, Hash>, 124 | // transactions root 125 | pub receipt_hash: Cow<'a, Hash>, 126 | // receipt_root 127 | pub bloom: Bloom, 128 | pub difficulty: Difficulty, 129 | pub height: Height, 130 | pub gas_limit: Gas, 131 | pub gas_used: Gas, 132 | pub time: Timestamp, 133 | #[serde(default)] 134 | pub extra: Option>>, 135 | } 136 | 137 | //impl HeaderBytes<'_> { 138 | // fn new(header: &Header) -> Self { 139 | // HeaderBytes { 140 | // prev_hash: Cow::from(&header.prev_hash), 141 | // proposer: header.proposer.clone(), 142 | // root: Cow::from(header.root), 143 | // tx_hash: Cow::from(header.tx_hash), 144 | // receipt_hash: Cow::from(header.receipt_hash), 145 | // bloom: header.bloom, 146 | // difficulty: header.difficulty, 147 | // height: header.height, 148 | // gas_limit: header.gas_limit, 149 | // gas_used: header.gas_used, 150 | // time: header.time, 151 | // extra: None, 152 | // } 153 | // } 154 | //} 155 | 156 | #[derive(Serialize, Deserialize, Debug, Clone)] 157 | pub struct Block { 158 | header: Header, 159 | transactions: Vec, 160 | } 161 | 162 | implement_cryptohash_traits! {Block} 163 | implement_storagevalue_traits! {Block} 164 | 165 | #[derive(Serialize, Deserialize, Debug, Clone)] 166 | pub struct Blocks(pub Vec); 167 | implement_cryptohash_traits! {Blocks} 168 | implement_storagevalue_traits! {Blocks} 169 | 170 | 171 | impl Block { 172 | pub fn new(header: Header, txs: Vec) -> Self { 173 | Block { 174 | header, 175 | transactions: txs, 176 | } 177 | } 178 | 179 | pub fn new2(header: Header, transactions: Vec) -> Self { 180 | Block { 181 | header: header, 182 | transactions: transactions, 183 | } 184 | } 185 | 186 | pub fn hash(&self) -> Hash { 187 | self.header.block_hash() 188 | } 189 | 190 | pub fn header(&self) -> &Header { 191 | &self.header 192 | } 193 | 194 | pub fn mut_header(&mut self) -> &mut Header { 195 | &mut self.header 196 | } 197 | 198 | pub fn height(&self) -> Height { self.header.height } 199 | 200 | pub fn transactions(&self) -> &Vec { 201 | &self.transactions 202 | } 203 | 204 | pub fn mut_transactions(&mut self) -> &mut Vec { 205 | &mut self.transactions 206 | } 207 | 208 | pub fn coinbase(&self) -> Address { 209 | let coinbase = self.header.proposer; 210 | coinbase 211 | } 212 | 213 | pub fn add_votes(&mut self, signatures: Vec) { 214 | let ref mut header = self.header; 215 | let votes = header.votes.get_or_insert(Votes::new(vec![])); 216 | votes.add_votes(&signatures); 217 | } 218 | 219 | pub fn votes(&self) -> Option<&Votes> { 220 | self.header.votes.as_ref() 221 | } 222 | 223 | pub fn mut_votes(&mut self) -> Option<&mut Votes> { 224 | self.header.votes.as_mut() 225 | } 226 | } 227 | 228 | #[cfg(test)] 229 | mod tests { 230 | use super::*; 231 | use std::io::{self, Write}; 232 | 233 | #[test] 234 | fn header() { 235 | let header = Header::zero_header(); 236 | writeln!(io::stdout(), "{:#?}", header).unwrap(); 237 | let j_str = serde_json::to_string(&header).unwrap(); 238 | writeln!(io::stdout(), "{}", j_str).unwrap(); 239 | } 240 | } -------------------------------------------------------------------------------- /src/types/mod.rs: -------------------------------------------------------------------------------- 1 | use cryptocurrency_kit::ethkey::Address; 2 | use cryptocurrency_kit::common::to_hex; 3 | use cryptocurrency_kit::crypto::{hash, CryptoHash, Hash, EMPTY_HASH}; 4 | use cryptocurrency_kit::storage::values::StorageValue; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use std::io::Cursor; 8 | use std::borrow::Cow; 9 | use std::collections::HashMap; 10 | 11 | use std::clone::Clone; 12 | use std::cmp::{Ord, Ordering, PartialEq}; 13 | use std::fmt::Display; 14 | 15 | pub mod transaction; 16 | pub mod block; 17 | pub mod votes; 18 | 19 | lazy_static! { 20 | pub static ref EMPTY_ADDRESS: Address = { 21 | Address::from(0) 22 | }; 23 | } 24 | 25 | pub type Height = u64; 26 | pub type Timestamp = u64; 27 | pub type Bloom = u64; 28 | pub type Difficulty = u64; 29 | pub type Gas = u64; 30 | 31 | pub type Validators = Vec; 32 | 33 | #[derive(Debug, Clone, Serialize, Deserialize)] 34 | pub struct ValidatorArray { 35 | inner: Vec
, 36 | index: HashMap, 37 | } 38 | 39 | implement_cryptohash_traits! {ValidatorArray} 40 | implement_storagevalue_traits! {ValidatorArray} 41 | 42 | #[derive(Debug, Clone, Serialize, Deserialize)] 43 | pub struct HashesEntry(pub Vec); 44 | 45 | implement_cryptohash_traits! {HashesEntry} 46 | implement_storagevalue_traits! {HashesEntry} 47 | 48 | impl ValidatorArray { 49 | pub fn new(addresses: Vec
) -> ValidatorArray { 50 | let mut index: HashMap = HashMap::new(); 51 | addresses.iter().fold(0, |acc, address| { 52 | index.insert(*address, acc as usize); 53 | acc + 1 54 | }); 55 | ValidatorArray { 56 | inner: addresses, 57 | index: index, 58 | } 59 | } 60 | 61 | pub fn have(&self, address: &Address) -> bool { 62 | self.index.contains_key(address) 63 | } 64 | } 65 | 66 | impl From> for ValidatorArray { 67 | fn from(validators: Vec) -> ValidatorArray { 68 | let addresses = validators.iter().map(|validator| { validator.address }).collect(); 69 | ValidatorArray::new(addresses) 70 | } 71 | } 72 | 73 | impl From> for ValidatorArray { 74 | fn from(addresses: Vec
) -> ValidatorArray { 75 | ValidatorArray::new(addresses) 76 | } 77 | } 78 | 79 | #[derive(Debug, Clone, Eq, Serialize, Deserialize)] 80 | pub struct Validator { 81 | address: Address, 82 | } 83 | 84 | implement_cryptohash_traits! {Validator} 85 | implement_storagevalue_traits! {Validator} 86 | 87 | impl Ord for Validator { 88 | fn cmp(&self, other: &Self) -> Ordering { 89 | self.address.cmp(&other.address) 90 | } 91 | } 92 | 93 | impl PartialOrd for Validator { 94 | fn partial_cmp(&self, other: &Self) -> Option { 95 | Some(self.cmp(other)) 96 | } 97 | } 98 | 99 | impl PartialEq for Validator { 100 | fn eq(&self, other: &Self) -> bool { 101 | self.address == other.address 102 | } 103 | } 104 | 105 | impl Display for Validator { 106 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 107 | write!(f, "{}", to_hex(self.address)) 108 | } 109 | } 110 | 111 | impl Validator { 112 | pub fn new(address: Address) -> Self { 113 | Validator { address } 114 | } 115 | 116 | pub fn address(&self) -> &Address { 117 | &self.address 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/types/transaction.rs: -------------------------------------------------------------------------------- 1 | use cryptocurrency_kit::crypto::{hash, CryptoHash, Hash}; 2 | use cryptocurrency_kit::ethkey::signature::*; 3 | use cryptocurrency_kit::ethkey::{Address, Secret, Signature}; 4 | use cryptocurrency_kit::storage::keys::StorageKey; 5 | use cryptocurrency_kit::storage::values::StorageValue; 6 | use serde::{Deserialize, Serialize}; 7 | use serde_json::to_string; 8 | 9 | use std::borrow::Cow; 10 | use std::io::Cursor; 11 | 12 | use crate::common::merkle_tree_root; 13 | use super::Gas; 14 | 15 | #[derive(Serialize, Deserialize, Debug, Clone)] 16 | pub struct Transaction { 17 | #[serde(rename = "nonce")] 18 | account_nonce: u64, 19 | #[serde(rename = "price")] 20 | gas_price: u64, 21 | gas_limit: Gas, 22 | recipient: Option
, 23 | amount: u64, 24 | #[serde(default)] 25 | payload: Vec, 26 | #[serde(rename = "sign")] 27 | signature: Option, 28 | #[serde(skip_serializing, skip_deserializing)] 29 | hash: Option, 30 | } 31 | 32 | impl CryptoHash for Transaction { 33 | fn hash(&self) -> Hash { 34 | hash(self.hash_payload()) 35 | } 36 | } 37 | 38 | implement_storagevalue_traits! {Transaction} 39 | 40 | impl Transaction { 41 | pub fn new( 42 | nonce: u64, 43 | to: Address, 44 | amount: u64, 45 | gas_limit: Gas, 46 | gas_price: u64, 47 | payload: Vec, 48 | ) -> Self { 49 | Transaction { 50 | account_nonce: nonce, 51 | gas_price: gas_price, 52 | gas_limit: gas_limit, 53 | recipient: Some(to), 54 | amount: amount, 55 | payload: payload, 56 | signature: None, 57 | hash: None, 58 | } 59 | } 60 | 61 | pub fn payload(&self) -> &[u8] { 62 | &self.payload 63 | } 64 | pub fn gas(&self) -> Gas { 65 | self.gas_limit 66 | } 67 | pub fn gas_price(&self) -> Gas { 68 | self.gas_price 69 | } 70 | pub fn amount(&self) -> u64 { 71 | self.amount 72 | } 73 | pub fn nonce(&self) -> u64 { 74 | self.account_nonce 75 | } 76 | pub fn to(&self) -> Option<&Address> { 77 | self.recipient.as_ref() 78 | } 79 | pub fn get_hash(&self) -> Option<&Hash> { 80 | self.hash.as_ref() 81 | } 82 | pub fn pretty_json(&self) -> String { 83 | to_string(self).unwrap() 84 | } 85 | 86 | /// TODO 87 | pub fn sign(&mut self, _chain_id: u64, secret: &Secret) { 88 | let signature = sign_bytes(secret, &TransactionSignature::packet_signature(&self)); 89 | self.signature = Some(signature.unwrap()); 90 | } 91 | 92 | pub fn verify_sign(&self, _chai_id: u64) -> bool { 93 | if self.signature.is_none() { 94 | return false; 95 | } 96 | let payload = self.signature_payload(); 97 | recover_bytes(self.signature.as_ref().unwrap(), &payload).is_ok() 98 | } 99 | 100 | pub fn set_hash(&mut self, hash: Hash) { 101 | self.hash = Some(hash) 102 | } 103 | 104 | pub fn set_signature(&mut self, sign: &Signature) { 105 | self.signature = Some(sign.clone()); 106 | } 107 | 108 | pub fn signature_payload(&self) -> Vec { 109 | TransactionSignature::packet_signature(&self) 110 | } 111 | 112 | pub fn hash_payload(&self) -> Vec { 113 | TransactionSignature::packet_hash(&self) 114 | } 115 | } 116 | 117 | impl Eq for Transaction {} 118 | 119 | impl PartialEq for Transaction { 120 | fn eq(&self, other: &Transaction) -> bool { 121 | self.get_hash().unwrap() == other.get_hash().unwrap() 122 | } 123 | } 124 | 125 | #[derive(Serialize, Deserialize, Debug, Clone)] 126 | struct TransactionSignature { 127 | #[serde(rename = "nonce")] 128 | account_nonce: u64, 129 | #[serde(rename = "price")] 130 | gas_price: u64, 131 | gas_limit: Gas, 132 | recipient: Address, 133 | amount: u64, 134 | #[serde(default)] 135 | payload: Vec, 136 | #[serde(rename = "sign")] 137 | signature: Option, 138 | } 139 | 140 | implement_storagevalue_traits! {TransactionSignature} 141 | implement_cryptohash_traits! {TransactionSignature} 142 | 143 | impl TransactionSignature { 144 | fn packet_hash(tx: &Transaction) -> Vec { 145 | let sign = tx.signature.as_ref().unwrap().clone(); 146 | let signature = TransactionSignature { 147 | account_nonce: tx.account_nonce, 148 | gas_price: tx.gas_price, 149 | gas_limit: tx.gas_limit, 150 | recipient: tx.recipient.unwrap(), 151 | amount: tx.amount, 152 | payload: tx.payload.clone(), 153 | signature: Some(sign), 154 | }; 155 | signature.into_bytes() 156 | } 157 | 158 | fn packet_signature(tx: &Transaction) -> Vec { 159 | let signature = TransactionSignature { 160 | account_nonce: tx.account_nonce, 161 | gas_price: tx.gas_price, 162 | gas_limit: tx.gas_limit, 163 | recipient: tx.recipient.unwrap(), 164 | amount: tx.amount, 165 | payload: tx.payload.clone(), 166 | signature: None, 167 | }; 168 | signature.into_bytes() 169 | } 170 | } 171 | 172 | 173 | pub fn merkle_root_transactions(transactions: Vec) -> Hash { 174 | merkle_tree_root(transactions) 175 | } 176 | 177 | #[cfg(test)] 178 | mod tests { 179 | use super::*; 180 | use cryptocurrency_kit::ethkey::random::Random; 181 | use cryptocurrency_kit::ethkey::{Generator, KeyPair}; 182 | use std::io::{self, Write}; 183 | 184 | #[test] 185 | fn transaction_sign() { 186 | let keypair = Random.generate().unwrap(); 187 | let mut tx = Transaction::new(10, Address::from(100), 89, 10, 90, vec![10, 39, 76, 31]); 188 | tx.sign(100, keypair.secret()); 189 | let hash = tx.hash(); 190 | writeln!(io::stdout(), "hash: {:?}", hash).unwrap(); 191 | writeln!(io::stdout(), "{}", tx.pretty_json()).unwrap(); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/types/votes.rs: -------------------------------------------------------------------------------- 1 | use byteorder::WriteBytesExt; 2 | use cryptocurrency_kit::crypto::{hash, CryptoHash, Hash, HASH_SIZE}; 3 | use cryptocurrency_kit::ethkey::Secret; 4 | use cryptocurrency_kit::ethkey::{public_to_address, recover_bytes}; 5 | use cryptocurrency_kit::ethkey::{Address, Public, Signature}; 6 | 7 | use crate::protocol::{GossipMessage, MessageType}; 8 | 9 | const SIGN_OP_OFFSET: usize = 0; 10 | const SIGN_ROUND_OFFSET: usize = 1; 11 | const SIGN_PACKET_SIZE: usize = 9; 12 | 13 | use std::io::Cursor; 14 | use std::io::Write; 15 | 16 | #[derive(Serialize, Deserialize, Debug, Clone)] 17 | pub struct Votes(Vec); 18 | 19 | impl Votes { 20 | pub fn new(votes: Vec) -> Self { 21 | Votes(votes) 22 | } 23 | 24 | pub fn len(&self) -> usize { 25 | self.0.len() 26 | } 27 | 28 | pub fn add_votes(&mut self, votes: &Vec) { 29 | for vote in votes { 30 | self.add_vote(vote); 31 | } 32 | } 33 | 34 | pub fn add_vote(&mut self, vote: &Signature) -> bool { 35 | let ok = self.0.iter().any(|e_vote| *e_vote == *vote); 36 | if ok { 37 | return ok; 38 | } 39 | self.0.push(vote.clone()); 40 | false 41 | } 42 | 43 | pub fn remove_vote(&mut self, vote: &Signature) -> bool { 44 | self.0.remove_item(&vote).is_some() 45 | } 46 | 47 | pub fn votes(&self) -> &Vec { 48 | &self.0 49 | } 50 | 51 | pub fn verify_signs(&self, digest: Hash, author: F) -> bool 52 | where 53 | F: Fn(Address) -> bool, 54 | { 55 | self.0.iter().all( 56 | |signature| { 57 | match decrypt_commit_bytes(digest.as_ref(), &signature) { 58 | Ok(address) => { 59 | author(address) 60 | } 61 | Err(_) => return false, 62 | } 63 | }, 64 | ) 65 | } 66 | } 67 | 68 | pub fn decrypt_commit_bytes>( 69 | input: T, 70 | signture: &Signature, 71 | ) -> Result { 72 | let input = input.as_ref(); 73 | if input.len() != SIGN_PACKET_SIZE { 74 | return Err("sign bytes size not equal SIGN_PACKET_SIZE".to_string()); 75 | } 76 | let op_code = MessageType::Commit; 77 | let digest = hash(input); 78 | let mut input = Cursor::new(vec![0_u8; 1 + HASH_SIZE]); 79 | input.write_u8(op_code as u8).unwrap(); 80 | input.write_all(digest.as_ref()).unwrap(); 81 | let buffer = input.into_inner(); 82 | let digest: Hash = hash(buffer); 83 | match recover_bytes(signture, digest.as_ref()) { 84 | Ok(ref public) => { 85 | let address = public_to_address(public); 86 | return Ok(address); 87 | } 88 | Err(_) => { 89 | return Err("recover commit sign failed".to_string()); 90 | } 91 | } 92 | } 93 | 94 | pub fn encrypt_commit_bytes(digest: &Hash, secret: &Secret) -> Signature { 95 | let mut input = Cursor::new(vec![0_u8; 1 + HASH_SIZE]); 96 | input.write_u8(MessageType::Commit as u8).unwrap(); 97 | input.write_all(digest.as_ref()).unwrap(); 98 | let buffer = input.into_inner(); 99 | let digest = hash(buffer); 100 | digest.sign(secret).unwrap() 101 | } 102 | 103 | 104 | #[cfg(test)] 105 | mod tests{ 106 | use super::*; 107 | use cryptocurrency_kit::ethkey::KeyPair; 108 | use cryptocurrency_kit::ethkey::Generator; 109 | use cryptocurrency_kit::ethkey::Random; 110 | 111 | #[test] 112 | fn t_random() { 113 | (0..10).for_each(|_|{ 114 | let keypair = Random{}.generate().unwrap(); 115 | println!("{:?}, {:?}", keypair, keypair.address()); 116 | }); 117 | } 118 | } -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | use ::actix::prelude::*; 2 | 3 | use std::time::Duration; 4 | 5 | #[derive(Message)] 6 | pub enum TimerOp { 7 | Stop 8 | } 9 | 10 | pub struct TimerRuntime { 11 | pub timeout: Duration, 12 | } 13 | 14 | impl Actor for TimerRuntime { 15 | type Context = Context; 16 | 17 | fn started(&mut self, ctx: &mut Context) { 18 | ctx.run_later(self.timeout, |_act, _| { 19 | System::current().stop(); 20 | ::std::process::exit(0); 21 | }); 22 | } 23 | } 24 | 25 | impl Handler for TimerRuntime { 26 | type Result = (); 27 | fn handle(&mut self, _msg: TimerOp, _: &mut Context) -> Self::Result { 28 | System::current().stop(); 29 | } 30 | } 31 | 32 | impl TimerRuntime { 33 | pub fn new(timeout: Duration) -> Addr { 34 | TimerRuntime::create(move |_ctx| { 35 | TimerRuntime { 36 | timeout: timeout, 37 | } 38 | }) 39 | } 40 | 41 | pub fn async_stop(pid: &Addr) { 42 | pid.do_send(TimerOp::Stop); 43 | } 44 | } --------------------------------------------------------------------------------