#include <stack>
#include "core.h"
using salticidae::DataStream;
using salticidae::static_pointer_cast;
using salticidae::get_hash;
#define LOG_INFO HOTSTUFF_LOG_INFO
#define LOG_DEBUG HOTSTUFF_LOG_DEBUG
#define LOG_WARN HOTSTUFF_LOG_WARN
namespace hotstuff {
void MsgHotStuff::gen_propose(const Proposal &proposal) {
DataStream s;
set_opcode(PROPOSE);
s << proposal;
set_payload(std::move(s));
}
void MsgHotStuff::parse_propose(Proposal &proposal) const {
DataStream(get_payload()) >> proposal;
}
void MsgHotStuff::gen_vote(const Vote &vote) {
DataStream s;
set_opcode(VOTE);
s << vote;
set_payload(std::move(s));
}
void MsgHotStuff::parse_vote(Vote &vote) const {
DataStream(get_payload()) >> vote;
}
void MsgHotStuff::gen_qfetchblk(const std::vector<uint256_t> &blk_hashes) {
DataStream s;
set_opcode(QUERY_FETCH_BLK);
gen_hash_list(s, blk_hashes);
set_payload(std::move(s));
}
void MsgHotStuff::parse_qfetchblk(std::vector<uint256_t> &blk_hashes) const {
DataStream s(get_payload());
parse_hash_list(s, blk_hashes);
}
void MsgHotStuff::gen_rfetchblk(const std::vector<block_t> &blks) {
DataStream s;
set_opcode(RESP_FETCH_BLK);
s << htole((uint32_t)blks.size());
for (auto blk: blks) s << *blk;
set_payload(std::move(s));
}
void MsgHotStuff::parse_rfetchblk(std::vector<block_t> &blks, HotStuffCore *hsc) const {
DataStream s;
uint32_t size;
s >> size;
size = letoh(size);
blks.resize(size);
for (auto &blk: blks)
{
Block _blk;
_blk.unserialize(s, hsc);
if (!_blk.verify(hsc->get_config()))
blk = hsc->storage->add_blk(std::move(_blk));
else
{
blk = nullptr;
LOG_WARN("block is invalid");
}
}
}
/* The core logic of HotStuff, is farily simple :) */
/*** begin HotStuff protocol logic ***/
HotStuffCore::HotStuffCore(ReplicaID id,
privkey_bt &&priv_key,
int32_t parent_limit):
b0(new Block(true, 1)),
bqc(b0),
bexec(b0),
vheight(0),
priv_key(std::move(priv_key)),
tails{bqc},
id(id),
parent_limit(parent_limit),
storage(new EntityStorage()) {
storage->add_blk(b0);
b0->qc_ref = b0;
}
void HotStuffCore::sanity_check_delivered(const block_t &blk) {
if (!blk->delivered)
throw std::runtime_error("block not delivered");
}
block_t HotStuffCore::sanity_check_delivered(const uint256_t &blk_hash) {
block_t blk = storage->find_blk(blk_hash);
if (blk == nullptr || !blk->delivered)
throw std::runtime_error("block not delivered");
return std::move(blk);
}
bool HotStuffCore::on_deliver_blk(const block_t &blk) {
if (blk->delivered)
{
LOG_WARN("attempt to deliver a block twice");
return false;
}
blk->parents.clear();
for (const auto &hash: blk->parent_hashes)
{
block_t p = sanity_check_delivered(hash);
blk->parents.push_back(p);
}
blk->height = blk->parents[0]->height + 1;
for (const auto &cmd: blk->cmds)
cmd->container = blk;
if (blk->qc)
{
block_t _blk = storage->find_blk(blk->qc->get_blk_hash());
if (_blk == nullptr)
throw std::runtime_error("block referred by qc not fetched");
blk->qc_ref = std::move(_blk);
} // otherwise blk->qc_ref remains null
for (auto pblk: blk->parents) tails.erase(pblk);
tails.insert(blk);
blk->delivered = true;
LOG_DEBUG("delivered %.10s", get_hex(blk->get_hash()).c_str());
return true;
}
void HotStuffCore::check_commit(const block_t &_blk) {
const block_t &blk = _blk->qc_ref;
if (blk->qc_ref == nullptr) return;
if (blk->decision) return;
block_t p = blk->parents[0];
if (p == blk->qc_ref)
{ /* commit */
std::vector<block_t> commit_queue;
block_t b;
for (b = p; b->height > bexec->height; b = b->parents[0])
{ /* todo: also commit the uncles/aunts */
commit_queue.push_back(b);
}
if (b != bexec)
throw std::runtime_error("safety breached :(");
for (auto it = commit_queue.rbegin(); it != commit_queue.rend(); it++)
{
const block_t &blk = *it;
blk->decision = 1;
#ifdef HOTSTUFF_ENABLE_LOG_PROTO
LOG_INFO("commit blk %.10s", get_hex10(blk->get_hash()).c_str());
#endif
for (auto cmd: blk->cmds)
do_decide(cmd);
}
bexec = p;
}
}
bool HotStuffCore::update(const uint256_t &bqc_hash) {
block_t _bqc = sanity_check_delivered(bqc_hash);
if (_bqc->qc_ref == nullptr) return false;
check_commit(_bqc);
if (_bqc->qc_ref->height > bqc->qc_ref->height)
bqc = _bqc;
return true;
}
void HotStuffCore::on_propose(const <