diff options
Diffstat (limited to 'include/hotstuff/entity.h')
-rw-r--r-- | include/hotstuff/entity.h | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/include/hotstuff/entity.h b/include/hotstuff/entity.h new file mode 100644 index 0000000..b3a0df4 --- /dev/null +++ b/include/hotstuff/entity.h @@ -0,0 +1,309 @@ +#ifndef _HOTSTUFF_ENT_H +#define _HOTSTUFF_ENT_H + +#include <vector> +#include <unordered_map> +#include <unordered_set> +#include <string> +#include <cstddef> +#include <ios> + +#include "salticidae/netaddr.h" +#include "salticidae/ref.h" +#include "type.h" +#include "util.h" +#include "crypto.h" + +namespace hotstuff { + +enum EntityType { + ENT_TYPE_CMD = 0x0, + ENT_TYPE_BLK = 0x1 +}; + +struct ReplicaInfo { + ReplicaID id; + salticidae::NetAddr addr; + pubkey_bt pubkey; + + ReplicaInfo(ReplicaID id, + const salticidae::NetAddr &addr, + pubkey_bt &&pubkey): + id(id), addr(addr), pubkey(std::move(pubkey)) {} + + ReplicaInfo(const ReplicaInfo &other): + id(other.id), addr(other.addr), + pubkey(other.pubkey->clone()) {} + + ReplicaInfo(ReplicaInfo &&other): + id(other.id), addr(other.addr), + pubkey(std::move(other.pubkey)) {} +}; + +class ReplicaConfig { + std::unordered_map<ReplicaID, ReplicaInfo> replica_map; + + public: + size_t nmajority; + + void add_replica(ReplicaID rid, const ReplicaInfo &info) { + replica_map.insert(std::make_pair(rid, info)); + } + + const ReplicaInfo &get_info(ReplicaID rid) const { + auto it = replica_map.find(rid); + if (it == replica_map.end()) + throw HotStuffError("rid %s not found", + get_hex(rid).c_str()); + return it->second; + } + + const PubKey &get_pubkey(ReplicaID rid) const { + return *(get_info(rid).pubkey); + } + + const salticidae::NetAddr &get_addr(ReplicaID rid) const { + return get_info(rid).addr; + } +}; + +class Block; +class HotStuffCore; + +using block_t = salticidae::RcObj<Block>; +using block_weak_t = salticidae::WeakObj<Block>; + +struct Finality: public Serializable { + int8_t decision; + uint256_t blk_hash; + + public: + Finality(): decision(0) {} + Finality(int8_t decision, uint256_t blk_hash): + decision(decision), blk_hash(blk_hash) {} + + void serialize(DataStream &s) const override { + s << decision; + if (decision == 1) s << blk_hash; + } + + void unserialize(DataStream &s) override { + s >> decision; + if (decision == 1) s >> blk_hash; + } +}; + +class Command: public Serializable { + friend HotStuffCore; + block_weak_t container; + public: + virtual ~Command() = default; + virtual const uint256_t &get_hash() const = 0; + virtual bool verify() const = 0; + inline int8_t get_decision() const; + inline Finality get_finality() const; + block_t get_container() const { + return container; + } +}; + +using command_t = RcObj<Command>; + +template<typename Hashable> +inline static std::vector<uint256_t> +get_hashes(const std::vector<Hashable> &plist) { + std::vector<uint256_t> hashes; + for (const auto &p: plist) + hashes.push_back(p->get_hash()); + return std::move(hashes); +} + +class Block { + friend HotStuffCore; + std::vector<uint256_t> parent_hashes; + std::vector<command_t> cmds; + quorum_cert_bt qc; + uint256_t hash; + + std::vector<block_t> parents; + block_t qc_ref; + quorum_cert_bt self_qc; + uint32_t height; + bool delivered; + int8_t decision; + + std::unordered_set<ReplicaID> voted; + + public: + Block(): + qc(nullptr), + qc_ref(nullptr), + self_qc(nullptr), height(0), + delivered(false), decision(0) {} + + Block(bool delivered, int8_t decision): + qc(nullptr), + hash(salticidae::get_hash(*this)), + qc_ref(nullptr), + self_qc(nullptr), height(0), + delivered(delivered), decision(decision) {} + + Block(const std::vector<block_t> &parents, + const std::vector<command_t> &cmds, + uint32_t height, + quorum_cert_bt &&qc, + const block_t &qc_ref, + quorum_cert_bt &&self_qc, + int8_t decision = 0): + parent_hashes(get_hashes(parents)), + cmds(cmds), + qc(std::move(qc)), + hash(salticidae::get_hash(*this)), + parents(parents), + qc_ref(qc_ref), + self_qc(std::move(self_qc)), + height(height), + delivered(0), + decision(decision) {} + + void serialize(DataStream &s) const; + + void unserialize(DataStream &s, HotStuffCore *hsc); + + const std::vector<command_t> &get_cmds() const { + return cmds; + } + + const std::vector<block_t> &get_parents() const { + return parents; + } + + const std::vector<uint256_t> &get_parent_hashes() const { + return parent_hashes; + } + + const uint256_t &get_hash() const { return hash; } + + bool verify(const ReplicaConfig &config) const { + if (qc && !qc->verify(config)) return false; + for (auto cmd: cmds) + if (!cmd->verify()) return false; + return true; + } + + int8_t get_decision() const { return decision; } + + bool is_delivered() const { return delivered; } + + uint32_t get_height() const { return height; } + + const quorum_cert_bt &get_qc() const { return qc; } + + operator std::string () const { + DataStream s; + s << "<block " + << "id=" << get_hex10(hash) << " " + << "height=" << std::to_string(height) << " " + << "parent=" << get_hex10(parent_hashes[0]) << ">"; + return std::string(std::move(s)); + } +}; + +struct BlockHeightCmp { + bool operator()(const block_t &a, const block_t &b) const { + return a->get_height() < b->get_height(); + } +}; + +int8_t Command::get_decision() const { + block_t cptr = container; + return cptr ? cptr->get_decision() : 0; +} + +Finality Command::get_finality() const { + block_t blk = get_container(); + return Finality(get_decision(), + blk ? blk->get_hash() : uint256_t()); +} + +class EntityStorage { + std::unordered_map<const uint256_t, block_t> blk_cache; + std::unordered_map<const uint256_t, command_t> cmd_cache; + public: + bool is_blk_delivered(const uint256_t &blk_hash) { + auto it = blk_cache.find(blk_hash); + if (it == blk_cache.end()) return false; + return it->second->is_delivered(); + } + + bool is_blk_fetched(const uint256_t &blk_hash) { + return blk_cache.count(blk_hash); + } + + const block_t &add_blk(Block &&_blk) { + block_t blk = new Block(std::move(_blk)); + return blk_cache.insert(std::make_pair(blk->get_hash(), blk)).first->second; + } + + const block_t &add_blk(const block_t &blk) { + return blk_cache.insert(std::make_pair(blk->get_hash(), blk)).first->second; + } + + block_t find_blk(const uint256_t &blk_hash) { + auto it = blk_cache.find(blk_hash); + return it == blk_cache.end() ? nullptr : it->second; + } + + bool is_cmd_fetched(const uint256_t &cmd_hash) { + return cmd_cache.count(cmd_hash); + } + + const command_t &add_cmd(const command_t &cmd) { + return cmd_cache.insert(std::make_pair(cmd->get_hash(), cmd)).first->second; + } + + command_t find_cmd(const uint256_t &cmd_hash) { + auto it = cmd_cache.find(cmd_hash); + return it == cmd_cache.end() ? nullptr: it->second; + } + + size_t get_cmd_cache_size() { + return cmd_cache.size(); + } + size_t get_blk_cache_size() { + return blk_cache.size(); + } + + bool try_release_cmd(const command_t &cmd) { + if (cmd.get_cnt() == 2) /* only referred by cmd and the storage */ + { + const auto &cmd_hash = cmd->get_hash(); + cmd_cache.erase(cmd_hash); + return true; + } + return false; + } + + bool try_release_blk(const block_t &blk) { + if (blk.get_cnt() == 2) /* only referred by blk and the storage */ + { + const auto &blk_hash = blk->get_hash(); +#ifdef HOTSTUFF_ENABLE_LOG_PROTO + HOTSTUFF_LOG_INFO("releasing blk %.10s", get_hex(blk_hash).c_str()); +#endif + for (const auto &cmd: blk->get_cmds()) + try_release_cmd(cmd); + blk_cache.erase(blk_hash); + return true; + } +#ifdef HOTSTUFF_ENABLE_LOG_PROTO + else + HOTSTUFF_LOG_INFO("cannot release (%lu)", blk.get_cnt()); +#endif + return false; + } +}; + +} + +#endif |