Skip to content

Commit

Permalink
Record votes
Browse files Browse the repository at this point in the history
Signed-off-by: Stanislav Frolov <stanislav@thirdhash.com>
  • Loading branch information
frolosofsky committed Apr 2, 2019
1 parent fcf28a4 commit 680a00b
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 38 deletions.
16 changes: 5 additions & 11 deletions src/esperanza/checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <esperanza/adminparams.h>
#include <esperanza/checks.h>
#include <esperanza/finalizationstate.h>
#include <finalization/vote_recorder.h>
#include <script/interpreter.h>
#include <script/standard.h>
#include <txmempool.h>
Expand Down Expand Up @@ -204,9 +203,9 @@ bool ContextualCheckLogoutTx(const CTransaction &tx, CValidationState &err_state
}

// We keep the check for the prev at the end because is the most expensive
// check (potentially goes to disk) and there is a good chance that if the
// vote is not valid (i.e. outdated) then the function will return before
// reaching this point.
// check and there is a good chance that if the vote is not valid (i.e. outdated)
// then the function will return before reaching this point.

TxType prev_tx_type = TxType::REGULAR;
CScript prev_out_script;

Expand Down Expand Up @@ -345,18 +344,13 @@ bool ContextualCheckVoteTx(const CTransaction &tx, CValidationState &err_state,
return false;
}

if (!finalization::RecordVote(tx, err_state)) {
return false;
}

if (fin_state.ValidateVote(vote) != +Result::SUCCESS) {
return err_state.DoS(10, false, REJECT_INVALID, "bad-vote-invalid-state");
}

// We keep the check for the prev at the end because is the most expensive
// check (potentially goes to disk) and there is a good chance that if the
// vote is not valid (i.e. outdated) then the function will return before
// reaching this point.
// check and there is a good chance that if the vote is not valid (i.e. outdated)
// then the function will return before reaching this point.

TxType prev_tx_type = TxType::REGULAR;
CScript prev_out_script;
Expand Down
12 changes: 5 additions & 7 deletions src/finalization/vote_recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,24 +133,22 @@ std::shared_ptr<VoteRecorder> VoteRecorder::GetVoteRecorder() {

CScript VoteRecord::GetScript() const { return CScript::EncodeVote(vote, sig); }

bool RecordVote(const CTransaction &tx, CValidationState &err_state) {
bool RecordVote(const CTransaction &tx,
CValidationState &err_state,
const FinalizationState &fin_state) {
assert(tx.IsVote());

LOCK(GetComponent<StateRepository>()->GetLock());
const FinalizationState *fin_state = GetComponent<StateRepository>()->GetTipState();
assert(fin_state != nullptr);

esperanza::Vote vote;
std::vector<unsigned char> voteSig;

if (!CScript::ExtractVoteFromVoteSignature(tx.vin[0].scriptSig, vote, voteSig)) {
return err_state.DoS(10, false, REJECT_INVALID, "bad-vote-data-format");
}
const esperanza::Result res = fin_state->ValidateVote(vote);
const esperanza::Result res = fin_state.ValidateVote(vote);

if (res != +esperanza::Result::ADMIN_BLACKLISTED &&
res != +esperanza::Result::VOTE_NOT_BY_VALIDATOR) {
finalization::VoteRecorder::GetVoteRecorder()->RecordVote(vote, voteSig, *fin_state);
finalization::VoteRecorder::GetVoteRecorder()->RecordVote(vote, voteSig, fin_state);
}

return true;
Expand Down
4 changes: 3 additions & 1 deletion src/finalization/vote_recorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ class VoteRecorder : private boost::noncopyable {
static std::shared_ptr<VoteRecorder> GetVoteRecorder();
};

bool RecordVote(const CTransaction &tx, CValidationState &err_state);
bool RecordVote(const CTransaction &tx,
CValidationState &err_state,
const FinalizationState &fin_state);

} // namespace finalization

Expand Down
29 changes: 20 additions & 9 deletions src/p2p/finalizer_commits_handler_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,12 @@ bool FinalizerCommitsHandlerImpl::OnCommits(

std::list<const CBlockIndex *> to_append;

const bool fast_sync = snapshot::IsISDEnabled() && snapshot::IsInitialSnapshotDownload();

const CBlockIndex *last_index = nullptr;
{
LOCK(m_active_chain->GetLock());
LOCK(m_repo->GetLock());

for (const HeaderAndFinalizerCommits &d : msg.data) {

Expand All @@ -340,15 +343,25 @@ bool FinalizerCommitsHandlerImpl::OnCommits(
}

// UNIT-E TODO: Store finalizer transactions somewhere.
// We cannot perform ContextualCheck now as it relies on GetTransaction which effectively
// loads prev transaction from the disk. During commits exchange we do not have such data
// on the disk.
// We cannot perform ContextualCheck now as it relies on UTXO lookup. During commits
// exchange we do not have such data.
// So, now just record the votes. ContextualCheck would be performed later after block
// arrives.

// In case of fast-sync record votes relying on the previously processed finalization state.
// Otherwise use the tip's state.

const finalization::FinalizationState *fin_state = nullptr;
if (fast_sync && new_index->pprev != nullptr) {
fin_state = m_repo->Find(*new_index->pprev);
} else {
fin_state = m_repo->GetTipState();
}
assert(fin_state != nullptr);

for (const auto &c : d.commits) {
if (c->IsVote()) {
if (!finalization::RecordVote(*c, err_state)) {
if (!finalization::RecordVote(*c, err_state, *fin_state)) {
return false;
}
}
Expand Down Expand Up @@ -383,8 +396,6 @@ bool FinalizerCommitsHandlerImpl::OnCommits(

blockchain::Height download_until = 0;

const bool snapshot_enabled = snapshot::IsISDEnabled() && snapshot::IsInitialSnapshotDownload();

{
LOCK(m_repo->GetLock());

Expand All @@ -396,7 +407,7 @@ bool FinalizerCommitsHandlerImpl::OnCommits(
const uint32_t index_epoch = index_state->GetLastFinalizedEpoch();
const uint32_t tip_epoch = tip_state->GetLastFinalizedEpoch();

if (!snapshot_enabled && index_epoch > tip_epoch) {
if (!fast_sync && index_epoch > tip_epoch) {
download_until = index_state->GetEpochCheckpointHeight(index_epoch + 1);
LogPrint(BCLog::NET, "Commits sync reached finalization at epoch=%d, mark blocks up to height %d to download\n",
index_epoch, download_until);
Expand All @@ -413,7 +424,7 @@ bool FinalizerCommitsHandlerImpl::OnCommits(
LogPrint(BCLog::NET, "Commits sync finished after processing header=%s, height=%d\n",
last_index->GetBlockHash().GetHex(), last_index->nHeight);
download_until = last_index->nHeight;
if (snapshot_enabled) {
if (fast_sync) {
snapshot::HeadersDownloaded();
}
break;
Expand All @@ -423,7 +434,7 @@ bool FinalizerCommitsHandlerImpl::OnCommits(
break;
}

if (snapshot_enabled) {
if (fast_sync) {
return true;
}

Expand Down
13 changes: 8 additions & 5 deletions src/snapshot/p2p_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ inline const CBlockIndex *LookupFinalizedBlockIndex(const uint256 &hash) {
return nullptr;
}

auto fin_repo = GetComponent<finalization::StateRepository>();
const finalization::FinalizationState *fin_state = fin_repo->GetTipState();
if (bi->nHeight <= fin_state->GetEpochCheckpointHeight(fin_state->GetLastFinalizedEpoch())) {
LogPrint(BCLog::SNAPSHOT, "%s: block=%s height=%d is not finalized\n", hash.GetHex(), bi->nHeight);
return nullptr;
{
auto fin_repo = GetComponent<finalization::StateRepository>();
LOCK(fin_repo->GetLock());
const finalization::FinalizationState *fin_state = fin_repo->GetTipState();
if (bi->nHeight <= fin_state->GetEpochCheckpointHeight(fin_state->GetLastFinalizedEpoch())) {
LogPrint(BCLog::SNAPSHOT, "%s: block=%s height=%d is not finalized\n", hash.GetHex(), bi->nHeight);
return nullptr;
}
}

return bi;
Expand Down
25 changes: 20 additions & 5 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include <boost/thread.hpp>

#include <esperanza/finalizationstate.h>
#include <finalization/vote_recorder.h>
#include <tinyformat.h>
#include <snapshot/snapshot_validation.h>

Expand Down Expand Up @@ -588,9 +589,18 @@ static BCLog::LogFlags GetTransactionLogCategory(const CTransaction &tx) {

static bool ContextualCheckFinalizerCommit(const CTransaction &tx, CValidationState &err_state,
const esperanza::FinalizationState &fin_state,
const esperanza::FinalizationState &tip_fin_state,
const CCoinsView &view) {
const auto log_cat = GetTransactionLogCategory(tx);
LogPrint(log_cat, "Checking %s with id %s\n", tx.GetType()._to_string(), tx.GetHash().GetHex());
if (tx.IsVote()) {
if (!esperanza::CheckVoteTx(tx, err_state, /*vote=*/nullptr, /*vote_sig=*/nullptr)) {
return false;
}
if (!finalization::RecordVote(tx, err_state, tip_fin_state)) {
return false;
}
}
if (!esperanza::ContextualCheckFinalizerCommit(tx, err_state, fin_state, view)) {
LogPrint(log_cat, "ERROR: %s (%s) check failed: %s\n", tx.GetType()._to_string(), tx.GetHash().GetHex(),
err_state.GetRejectReason());
Expand All @@ -602,10 +612,11 @@ static bool ContextualCheckFinalizerCommit(const CTransaction &tx, CValidationSt
static bool ContextualCheckBlockFinalizerCommits(const CBlock &block,
CValidationState &err_state,
const esperanza::FinalizationState &fin_state,
const esperanza::FinalizationState &tip_fin_state,
const CCoinsView &view) {
for (const auto &tx : block.vtx) {
if (tx->IsFinalizerCommit()) {
if (!::ContextualCheckFinalizerCommit(*tx, err_state, fin_state, view)) {
if (!::ContextualCheckFinalizerCommit(*tx, err_state, fin_state, tip_fin_state, view)) {
return false;
}
}
Expand Down Expand Up @@ -673,7 +684,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
GetComponent<finalization::StateRepository>()->GetTipState();
assert(fin_state != nullptr);
if (tx.IsFinalizerCommit() &&
!::ContextualCheckFinalizerCommit(tx, state, *fin_state, view)) {
!::ContextualCheckFinalizerCommit(tx, state, *fin_state, *fin_state, view)) {
return false; // state already filled by ContextualCheckFinalizerTx
}

Expand Down Expand Up @@ -2009,13 +2020,17 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
}

{
LOCK(GetComponent<finalization::StateRepository>()->GetLock());
auto repo = GetComponent<finalization::StateRepository>();
LOCK(repo->GetLock());
const finalization::FinalizationState *fin_state = nullptr;
if (pindex->pprev != nullptr) {
fin_state = GetComponent<finalization::StateRepository>()->Find(*pindex->pprev);
fin_state = repo->Find(*pindex->pprev);
}
assert(fin_state != nullptr || isGenesisBlock || !has_finalization_tx);

const finalization::FinalizationState *tip_fin_state = repo->GetTipState();
assert(tip_fin_state != nullptr || isGenesisBlock);

// UNIT-E: We need to check finalization transactions prior check queue control in order to avoid
// deadlock between threads.
//
Expand All @@ -2029,7 +2044,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (!isGenesisBlock &&
has_finalization_tx &&
!ContextualCheckBlockFinalizerCommits(
block,state, *fin_state, view)) {
block,state, *fin_state, *tip_fin_state, view)) {
return false;
}
}
Expand Down

0 comments on commit 680a00b

Please sign in to comment.