From b5bdaad82bdd04934bb32b78db2c10bd4f1e8b0c Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Thu, 25 Apr 2019 17:13:41 +0200 Subject: [PATCH 01/11] Finalize the latest epoch When we have two consecutive justifications, finalize the latest one instead of the previous one. Signed-off-by: Kostiantyn Stepaniuk --- src/Makefile.test.include | 1 + src/esperanza/finalizationstate.cpp | 50 ++--- src/esperanza/finalizationstate.h | 4 +- src/finalization/state_processor.cpp | 11 +- src/finalization/state_repository.cpp | 4 +- src/test/esperanza/checks_tests.cpp | 4 +- ...nstate_calculate_withdraw_amount_tests.cpp | 127 +++++++++++ .../finalizationstate_deposit_tests.cpp | 2 +- .../finalizationstate_logout_tests.cpp | 6 +- .../finalizationstate_slash_tests.cpp | 10 +- .../esperanza/finalizationstate_tests.cpp | 13 +- .../esperanza/finalizationstate_utils.cpp | 15 +- src/test/esperanza/finalizationstate_utils.h | 3 + .../finalizationstate_vote_tests.cpp | 59 ++---- .../finalizationstate_withdraw_tests.cpp | 17 +- .../finalization/state_processor_tests.cpp | 51 ++--- .../finalization/state_repository_tests.cpp | 60 +++--- src/validation.cpp | 22 +- test/functional/feature_commits_forkchoice.py | 17 +- test/functional/feature_finalizer.py | 18 +- .../feature_fork_choice_finalization.py | 187 +++++++++-------- ...ature_fork_choice_forked_finalize_epoch.py | 198 +++++++++--------- ...ure_fork_choice_parallel_justifications.py | 135 ++++++------ test/functional/feature_reindex_commits.py | 13 +- test/functional/feature_snapshot.py | 13 +- test/functional/feature_snapshot_creation.py | 2 +- .../feature_snapshot_finalization.py | 8 +- test/functional/finalization_deposit.py | 14 +- test/functional/finalization_deposit_reorg.py | 10 +- .../finalization_expired_vote_conflict.py | 10 +- test/functional/finalization_logout.py | 63 +++--- test/functional/finalization_slash.py | 88 ++++---- test/functional/finalization_slash_itself.py | 60 +++--- test/functional/finalization_slash_restart.py | 8 +- .../finalization_state_restoration.py | 14 +- test/functional/finalization_vote.py | 52 ++--- test/functional/finalization_vote_reorg.py | 136 ++++++------ test/functional/finalization_withdraw.py | 136 ++++++------ test/functional/mempool_resurrect.py | 4 +- test/functional/p2p_commits.py | 51 +++-- test/functional/p2p_snapshot.py | 19 +- test/functional/rpc_finalization.py | 164 ++++++++------- test/functional/rpc_preciousblock.py | 7 +- 43 files changed, 986 insertions(+), 900 deletions(-) create mode 100644 src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 721973bbbe..7066ad89fe 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -75,6 +75,7 @@ UNITE_TESTS =\ test/esperanza/adminstate_tests.cpp \ test/esperanza/checks_tests.cpp \ test/esperanza/finalizationstate_tests.cpp \ + test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp \ test/esperanza/finalizationstate_deposit_tests.cpp \ test/esperanza/finalizationstate_vote_tests.cpp \ test/esperanza/finalizationstate_logout_tests.cpp \ diff --git a/src/esperanza/finalizationstate.cpp b/src/esperanza/finalizationstate.cpp index effae06883..1d98f611b7 100644 --- a/src/esperanza/finalizationstate.cpp +++ b/src/esperanza/finalizationstate.cpp @@ -76,10 +76,7 @@ Result FinalizationState::InitializeEpoch(blockchain::Height blockHeight) { assert(m_settings.IsEpochStart(blockHeight) && "provided blockHeight is not the first block of a new epoch"); - IncrementDynasty(); - const uint32_t new_epoch = GetEpoch(blockHeight); - if (new_epoch != m_current_epoch + 1) { return fail(Result::INIT_WRONG_EPOCH, /*log_errors=*/true, @@ -119,10 +116,15 @@ Result FinalizationState::InitializeEpoch(blockchain::Height blockHeight) { } } else { - InstaJustify(); m_reward_factor = 0; } + IncrementDynasty(); + + if (GetActiveFinalizers().empty()) { + InstaJustify(); + } + std::string log_msg; if (m_current_epoch >= 2 && m_last_justified_epoch != m_current_epoch - 2) { log_msg = " epoch=" + std::to_string(m_current_epoch - 2) + " was not justified."; @@ -146,10 +148,10 @@ void FinalizationState::InstaJustify() { m_last_justified_epoch = m_current_epoch - 1; if (m_current_epoch > 1) { - uint32_t expected_finalized = m_current_epoch - 2; - if (GetCheckpoint(expected_finalized).m_is_justified) { - GetCheckpoint(expected_finalized).m_is_finalized = true; - m_last_finalized_epoch = expected_finalized; + uint32_t prev_justified = m_current_epoch - 2; + if (GetCheckpoint(prev_justified).m_is_justified) { + cp.m_is_finalized = true; + m_last_finalized_epoch = m_last_justified_epoch; } } @@ -159,7 +161,7 @@ void FinalizationState::InstaJustify() { void FinalizationState::IncrementDynasty() { // finalized epoch is m_current_epoch - 2 because: - // finalized (0) - justified (1) - votes to justify (2) + // justified(0) - finalized(1) - votes to justify(2) - m_current_epoch(3) // skip dynasty increment for the hardcoded finalized epoch=0 // as it's already "considered" incremented from -1 to 0. @@ -172,7 +174,7 @@ void FinalizationState::IncrementDynasty() { m_current_dynasty += 1; m_prev_dyn_deposits = m_cur_dyn_deposits; m_cur_dyn_deposits += GetDynastyDelta(m_current_dynasty); - m_dynasty_start_epoch[m_current_dynasty] = m_current_epoch + 1; + m_dynasty_start_epoch[m_current_dynasty] = m_current_epoch; for (auto it = m_checkpoints.begin(); it != m_checkpoints.end();) { if (it->first < m_last_finalized_epoch) { @@ -187,7 +189,6 @@ void FinalizationState::IncrementDynasty() { } ufp64::ufp64_t FinalizationState::GetCollectiveRewardFactor() { - uint32_t epoch = m_current_epoch; bool isLive = GetEpochsSinceFinalization() <= 2; if (!DepositExists() || !isLive) { @@ -195,11 +196,11 @@ ufp64::ufp64_t FinalizationState::GetCollectiveRewardFactor() { } ufp64::ufp64_t curVoteFraction = ufp64::div_2uint( - GetCheckpoint(epoch - 1).GetCurDynastyVotes(m_expected_source_epoch), + GetCheckpoint(m_current_epoch - 2).GetCurDynastyVotes(m_expected_source_epoch - 1), m_cur_dyn_deposits); ufp64::ufp64_t prevVoteFraction = ufp64::div_2uint( - GetCheckpoint(epoch - 1).GetPrevDynastyVotes(m_expected_source_epoch), + GetCheckpoint(m_current_epoch - 2).GetPrevDynastyVotes(m_expected_source_epoch - 1), m_prev_dyn_deposits); ufp64::ufp64_t voteFraction = ufp64::min(curVoteFraction, prevVoteFraction); @@ -208,7 +209,7 @@ ufp64::ufp64_t FinalizationState::GetCollectiveRewardFactor() { } bool FinalizationState::DepositExists() const { - return m_cur_dyn_deposits > 0; + return m_cur_dyn_deposits > 0 && m_prev_dyn_deposits > 0; } ufp64::ufp64_t FinalizationState::GetSqrtOfTotalDeposits() const { @@ -244,6 +245,10 @@ uint64_t FinalizationState::GetDepositSize(const uint160 &validatorAddress) cons } } +uint32_t FinalizationState::GetExpectedSourceEpoch() const { + return m_expected_source_epoch; +} + uint32_t FinalizationState::GetRecommendedTargetEpoch() const { return m_recommended_target_epoch; } @@ -418,7 +423,7 @@ void FinalizationState::ProcessDeposit(const uint160 &validatorAddress, CAmount depositValue) { LOCK(cs_esperanza); - uint32_t startDynasty = m_current_dynasty + 3; + uint32_t startDynasty = m_current_dynasty + 2; uint64_t scaledDeposit = ufp64::div_to_uint(static_cast(depositValue), GetDepositScaleFactor(m_current_epoch)); @@ -538,8 +543,8 @@ void FinalizationState::ProcessVote(const Vote &vote) { targetEpoch); if (targetEpoch == sourceEpoch + 1) { - GetCheckpoint(sourceEpoch).m_is_finalized = true; - m_last_finalized_epoch = sourceEpoch; + GetCheckpoint(targetEpoch).m_is_finalized = true; + m_last_finalized_epoch = targetEpoch; LogPrint(BCLog::FINALIZATION, "%s: epoch=%d finalized.\n", __func__, sourceEpoch); } @@ -862,17 +867,6 @@ uint32_t FinalizationState::GetCurrentDynasty() const { return m_current_dynasty; } -uint32_t FinalizationState::GetCheckpointHeightAfterFinalizedEpoch() const { - const uint32_t epoch = m_last_finalized_epoch + 1; - if (m_last_finalized_epoch != 0) { - // epoch=0 is self-finalized and doesn't require - // parent epoch to justify it but for other epochs - // this rule must hold - assert(GetCheckpoint(epoch).m_is_justified); - } - return GetEpochCheckpointHeight(epoch); -} - uint32_t FinalizationState::GetEpochLength() const { return m_settings.epoch_length; } diff --git a/src/esperanza/finalizationstate.h b/src/esperanza/finalizationstate.h index b28e385d57..0421b9e041 100644 --- a/src/esperanza/finalizationstate.h +++ b/src/esperanza/finalizationstate.h @@ -131,11 +131,9 @@ class FinalizationState : public FinalizationStateData { uint32_t GetCurrentEpoch() const; uint32_t GetCurrentDynasty() const; - //! \brief Returns the height of checkpoint next to last finalized checkpoint. It must be justified. - uint32_t GetCheckpointHeightAfterFinalizedEpoch() const; - uint64_t GetDepositSize(const uint160 &validatorAddress) const; + uint32_t GetExpectedSourceEpoch() const; uint32_t GetRecommendedTargetEpoch() const; Vote GetRecommendedVote(const uint160 &validatorAddress) const; diff --git a/src/finalization/state_processor.cpp b/src/finalization/state_processor.cpp index 43b975f1b5..70c94bd6d3 100644 --- a/src/finalization/state_processor.cpp +++ b/src/finalization/state_processor.cpp @@ -124,15 +124,10 @@ bool ProcessorImpl::ProcessNewTip(const CBlockIndex &block_index, const CBlock & assert(state); // We cannot make forks before this point as they can revert finalization. - const uint32_t trim_until = state->GetCheckpointHeightAfterFinalizedEpoch(); + const uint32_t checkpoint_height = state->GetEpochCheckpointHeight(state->GetLastFinalizedEpoch()); + m_repo->TrimUntilHeight(checkpoint_height); - // for 0 epoch it will be in the future - if (static_cast(block_index.nHeight) > trim_until) { - m_repo->TrimUntilHeight(trim_until); - } - - const uint32_t checkpoint = state->GetEpochCheckpointHeight(state->GetLastFinalizedEpoch()); - snapshot::Creator::FinalizeSnapshots(m_active_chain->AtHeight(checkpoint)); + snapshot::Creator::FinalizeSnapshots(m_active_chain->AtHeight(checkpoint_height)); } return true; } diff --git a/src/finalization/state_repository.cpp b/src/finalization/state_repository.cpp index 68abc5c254..c10b238fed 100644 --- a/src/finalization/state_repository.cpp +++ b/src/finalization/state_repository.cpp @@ -185,7 +185,7 @@ bool RepositoryImpl::LoadStatesFromDB() { LogPrint(BCLog::FINALIZATION, "Restoring state repository from disk, last_finalized_epoch=%d\n", *last_finalized_epoch); const blockchain::Height height = - m_finalization_params->GetEpochCheckpointHeight(*last_finalized_epoch + 1); + m_finalization_params->GetEpochCheckpointHeight(*last_finalized_epoch); m_state_db->LoadStatesHigherThan(height, &m_states); if (!m_states.empty()) { return true; @@ -212,7 +212,7 @@ void RepositoryImpl::CheckAndRecover(Dependency pr const uint32_t last_finalized_epoch = state->GetLastFinalizedEpoch(); - const blockchain::Height height = last_finalized_epoch == 0 ? 0 : m_finalization_params->GetEpochCheckpointHeight(last_finalized_epoch + 1); + const blockchain::Height height = m_finalization_params->GetEpochCheckpointHeight(last_finalized_epoch); std::set unrecoverable; diff --git a/src/test/esperanza/checks_tests.cpp b/src/test/esperanza/checks_tests.cpp index 941ce479b2..9e507fc747 100644 --- a/src/test/esperanza/checks_tests.cpp +++ b/src/test/esperanza/checks_tests.cpp @@ -742,7 +742,7 @@ BOOST_AUTO_TEST_CASE(ContextualCheckVoteTx_test) { CPubKey pub_key = key.GetPubKey(); uint160 validator_address = pub_key.GetID(); - Vote vote_out{pub_key.GetID(), target_hash, 4, 5}; + Vote vote_out{pub_key.GetID(), target_hash, 2, 3}; std::vector vote_sig_out; BOOST_REQUIRE(CreateVoteSignature(&keystore, vote_out, vote_sig_out)); @@ -1036,7 +1036,7 @@ BOOST_AUTO_TEST_CASE(IsVoteExpired_test) { Vote current{RandValidatorAddr(), target_hash, 0, 5}; BOOST_CHECK_EQUAL(IsVoteExpired(CreateVoteTx(current, k), spy), false); - Vote afterLastFinalization{RandValidatorAddr(), target_hash, 0, 3}; + Vote afterLastFinalization{RandValidatorAddr(), target_hash, 0, 2}; BOOST_CHECK_EQUAL(IsVoteExpired(CreateVoteTx(afterLastFinalization, k), spy), true); Vote future{RandValidatorAddr(), target_hash, 0, 12}; diff --git a/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp b/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp new file mode 100644 index 0000000000..014e58c69d --- /dev/null +++ b/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2019 The Unit-e developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +BOOST_FIXTURE_TEST_SUITE(finalizationstate_calculate_withdraw_amount_tests, TestingSetup) + +void CreateAndProcessVote(FinalizationStateSpy &state, uint160 finalizer_address, uint256 target_hash) { + Vote vote{finalizer_address, target_hash, state.GetExpectedSourceEpoch(), state.GetRecommendedTargetEpoch()}; + BOOST_REQUIRE_EQUAL(state.ValidateVote(vote), +Result::SUCCESS); + uint32_t prev_justified = state.GetLastJustifiedEpoch(); + state.ProcessVote(vote); + BOOST_REQUIRE_EQUAL(state.GetLastJustifiedEpoch(), prev_justified + 1); +} + +void InitializeNextEpoch(FinalizationStateSpy &state) { + state.SetExpectedSourceEpoch(state.GetLastJustifiedEpoch()); + state.SetRecommendedTargetEpoch(state.GetCurrentEpoch()); + + Result res = state.InitializeEpoch(1 + state.GetCurrentEpoch() * state.EpochLength()); + BOOST_REQUIRE_EQUAL(res, +Result::SUCCESS); +} + +BOOST_AUTO_TEST_CASE(calculate_withdraw_amount) { + struct TestCase { + uint32_t epochs_before_logout; + CAmount deposit_amount; + CAmount withdraw_amount; + }; + + std::vector test_cases{ + TestCase{ + 0, + 1000000000000, + 1000011069779, + }, + TestCase{ + 1, + 1000000000000, + 1000014759724, + }, + TestCase{ + 10, + 1000000000000, + 1000047969221, + }, + TestCase{ + 100, + 1000000000000, + 1000380063832, + }, + }; + + for (size_t test_idx = 0; test_idx < test_cases.size(); ++test_idx) { + TestCase test_case = test_cases[test_idx]; + + // setup + finalization::Params params = finalization::Params::TestNet(); + FinalizationStateSpy state(params); + + // mock target hash + uint256 target_hash = GetRandHash(); + CBlockIndex block_index; + block_index.phashBlock = &target_hash; + state.SetRecommendedTarget(block_index); + + // deposit + uint160 finalizer_address = RandValidatorAddr(); + state.CreateAndActivateDeposit(finalizer_address, test_case.deposit_amount); + + // vote before logout + uint32_t end = state.GetCurrentEpoch() + test_case.epochs_before_logout; + for (uint32_t i = state.GetCurrentEpoch(); i < end; ++i) { + CreateAndProcessVote(state, finalizer_address, target_hash); + InitializeNextEpoch(state); + } + + // logout + BOOST_REQUIRE_EQUAL(state.ValidateLogout(finalizer_address), +Result::SUCCESS); + state.ProcessLogout(finalizer_address); + BOOST_REQUIRE_EQUAL(state.GetCurrentEpoch(), 4 + test_case.epochs_before_logout); + BOOST_REQUIRE_EQUAL(state.GetCurrentDynasty(), 2 + test_case.epochs_before_logout); + + // vote during logout delay + uint32_t end_logout = state.GetCurrentEpoch() + static_cast(state.DynastyLogoutDelay()); + BOOST_REQUIRE_EQUAL(end_logout, 9 + test_case.epochs_before_logout); + + const esperanza::Validator *finalizer = state.GetValidator(finalizer_address); + BOOST_REQUIRE(finalizer); + + for (uint32_t i = state.GetCurrentEpoch(); i <= end_logout; ++i) { + CreateAndProcessVote(state, finalizer_address, target_hash); + InitializeNextEpoch(state); + } + + // wait withdraw delay + uint32_t end_withdraw = end_logout + 1 + static_cast(state.WithdrawalEpochDelay()); + BOOST_REQUIRE_EQUAL(end_withdraw, 20 + test_case.epochs_before_logout); + + for (uint32_t i = state.GetCurrentEpoch(); i < end_withdraw; ++i) { + BOOST_REQUIRE_EQUAL(state.ValidateWithdraw(finalizer_address, test_case.deposit_amount), +Result::WITHDRAW_TOO_EARLY); + CAmount amount; + BOOST_REQUIRE_EQUAL(state.CalculateWithdrawAmount(finalizer_address, amount), +Result::WITHDRAW_TOO_EARLY); + InitializeNextEpoch(state); + } + + // test amount + BOOST_REQUIRE_EQUAL(state.ValidateWithdraw(finalizer_address, test_case.deposit_amount), +Result::SUCCESS); + BOOST_REQUIRE_EQUAL(state.GetLastFinalizedEpoch(), state.GetCurrentEpoch() - 1); + + for (uint32_t i = 0; i < 3; ++i) { + CAmount amount; + BOOST_CHECK_MESSAGE(state.CalculateWithdrawAmount(finalizer_address, amount) == +esperanza::Result::SUCCESS, + strprintf("test_case=%i: loop=%i: cannot calculate withdraw amount", test_idx, i)); + BOOST_CHECK_MESSAGE(amount == test_case.withdraw_amount, + strprintf("test_case=%i: loop=%i: amount: expected=%d received=%d", + test_idx, + i, + test_case.withdraw_amount, + amount)); + InitializeNextEpoch(state); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/esperanza/finalizationstate_deposit_tests.cpp b/src/test/esperanza/finalizationstate_deposit_tests.cpp index 49d57938f9..32dad10fe8 100644 --- a/src/test/esperanza/finalizationstate_deposit_tests.cpp +++ b/src/test/esperanza/finalizationstate_deposit_tests.cpp @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(process_deposit_tx) { BOOST_CHECK(it != validators.end()); Validator validator = it->second; - BOOST_CHECK_EQUAL(validator.m_start_dynasty, 3); // assuming we start from 0 + BOOST_CHECK_EQUAL(validator.m_start_dynasty, 2); // assuming we start from 0 BOOST_CHECK(validator.m_deposit > 0); BOOST_CHECK_EQUAL(it->first.GetHex(), validatorAddress.GetHex()); } diff --git a/src/test/esperanza/finalizationstate_logout_tests.cpp b/src/test/esperanza/finalizationstate_logout_tests.cpp index 84dde1a908..669d63700a 100644 --- a/src/test/esperanza/finalizationstate_logout_tests.cpp +++ b/src/test/esperanza/finalizationstate_logout_tests.cpp @@ -47,8 +47,8 @@ BOOST_AUTO_TEST_CASE(validate_logout_already_logged_out) { BOOST_CHECK_EQUAL(spy.ValidateLogout(validatorAddress), +Result::SUCCESS); spy.ProcessLogout(validatorAddress); - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 6 * spy.EpochLength()), +Result::SUCCESS); - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 7 * spy.EpochLength()), +Result::SUCCESS); + BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 4 * spy.EpochLength()), +Result::SUCCESS); + BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 5 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.ValidateLogout(validatorAddress), +Result::LOGOUT_ALREADY_DONE); } @@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(process_logout_end_dynasty) { std::map validators = spy.Validators(); Validator validator = validators.find(validatorAddress)->second; - BOOST_CHECK_EQUAL(8, validator.m_end_dynasty); + BOOST_CHECK_EQUAL(7, validator.m_end_dynasty); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/esperanza/finalizationstate_slash_tests.cpp b/src/test/esperanza/finalizationstate_slash_tests.cpp index 6792b0ed84..2c204ca625 100644 --- a/src/test/esperanza/finalizationstate_slash_tests.cpp +++ b/src/test/esperanza/finalizationstate_slash_tests.cpp @@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(is_slashable_same_vote) { block_index.phashBlock = &targetHash; spy.SetRecommendedTarget(block_index); - for (uint32_t i = 6; i < 8; ++i) { + for (uint32_t i = 4; i < 6; ++i) { Vote vote{validatorAddress, targetHash, i - 2, i - 1}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); spy.ProcessVote(vote); @@ -113,8 +113,8 @@ BOOST_AUTO_TEST_CASE(is_slashable_already_slashed) { block_index.phashBlock = &targetHash; spy.SetRecommendedTarget(block_index); - uint32_t i = 6; - for (; i < 8; ++i) { + uint32_t i = 4; + for (; i < 6; ++i) { Vote vote{validatorAddress, targetHash, i - 2, i - 1}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); @@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(process_slash_duplicate_vote) { block_index.phashBlock = &targetHash; spy.SetRecommendedTarget(block_index); - for (uint32_t i = 6; i < 8; ++i) { + for (uint32_t i = 4; i < 6; ++i) { Vote vote{validatorAddress, targetHash, i - 2, i - 1}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); spy.ProcessVote(vote); @@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE(process_slash_surrounding_vote) { block_index.phashBlock = &targetHash; spy.SetRecommendedTarget(block_index); - for (uint32_t i = 6; i < 8; ++i) { + for (uint32_t i = 4; i < 6; ++i) { Vote vote{validatorAddress, targetHash, i - 2, i - 1}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); spy.ProcessVote(vote); diff --git a/src/test/esperanza/finalizationstate_tests.cpp b/src/test/esperanza/finalizationstate_tests.cpp index 44ad12146c..56c4d49750 100644 --- a/src/test/esperanza/finalizationstate_tests.cpp +++ b/src/test/esperanza/finalizationstate_tests.cpp @@ -43,7 +43,6 @@ BOOST_AUTO_TEST_CASE(initialize_epoch_wrong_height_passed) { } BOOST_AUTO_TEST_CASE(initialize_epoch_insta_justify) { - finalization::Params params; FinalizationStateSpy spy(params); BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 0); @@ -60,19 +59,19 @@ BOOST_AUTO_TEST_CASE(initialize_epoch_insta_justify) { BOOST_CHECK_EQUAL(res, +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 2); BOOST_CHECK_EQUAL(spy.GetLastJustifiedEpoch(), 1); - BOOST_CHECK_EQUAL(spy.GetLastFinalizedEpoch(), 0); + BOOST_CHECK_EQUAL(spy.GetLastFinalizedEpoch(), 1); res = spy.InitializeEpoch(1 + 2 * spy.EpochLength()); BOOST_CHECK_EQUAL(res, +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 3); BOOST_CHECK_EQUAL(spy.GetLastJustifiedEpoch(), 2); - BOOST_CHECK_EQUAL(spy.GetLastFinalizedEpoch(), 1); + BOOST_CHECK_EQUAL(spy.GetLastFinalizedEpoch(), 2); res = spy.InitializeEpoch(1 + 3 * spy.EpochLength()); BOOST_CHECK_EQUAL(res, +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 4); BOOST_CHECK_EQUAL(spy.GetLastJustifiedEpoch(), 3); - BOOST_CHECK_EQUAL(spy.GetLastFinalizedEpoch(), 2); + BOOST_CHECK_EQUAL(spy.GetLastFinalizedEpoch(), 3); } // This tests assumes block time of 4s, hence epochs every 200s, and return of @@ -81,11 +80,11 @@ BOOST_AUTO_TEST_CASE(initialize_epoch_reward_factor) { finalization::Params params = finalization::Params::TestNet(); FinalizationStateSpy spy(params); + BOOST_CHECK_EQUAL(spy.InitializeEpoch(1), +Result::SUCCESS); *spy.CurDynDeposits() = 150000000; *spy.PrevDynDeposits() = 150000000; - - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1), +Result::SUCCESS); - BOOST_CHECK_EQUAL("0.00057174", ufp64::to_str(*spy.RewardFactor())); + BOOST_CHECK_EQUAL(spy.InitializeEpoch(51), +Result::SUCCESS); + BOOST_CHECK_EQUAL("0.00057194", ufp64::to_str(*spy.RewardFactor())); } // GetRecommendedVote tests diff --git a/src/test/esperanza/finalizationstate_utils.cpp b/src/test/esperanza/finalizationstate_utils.cpp index 36f277c9ff..fa13673d3d 100644 --- a/src/test/esperanza/finalizationstate_utils.cpp +++ b/src/test/esperanza/finalizationstate_utils.cpp @@ -93,16 +93,21 @@ void FinalizationStateSpy::CreateAndActivateDeposit(const uint160 &validator_add ProcessDeposit(validator_address, deposit_size); - for (uint32_t i = 1; i < 6 * EpochLength() + 1; i += EpochLength()) { + for (uint32_t i = 1; i < 4 * EpochLength() + 1; i += EpochLength()) { BOOST_REQUIRE_EQUAL(GetActiveFinalizers().size(), 0); + m_expected_source_epoch = m_last_justified_epoch; + m_recommended_target_epoch = m_current_epoch; + res = InitializeEpoch(i); BOOST_REQUIRE_EQUAL(res, +Result::SUCCESS); } - BOOST_REQUIRE_EQUAL(GetCurrentDynasty(), 3); - BOOST_REQUIRE_EQUAL(GetCurrentEpoch(), 6); - BOOST_REQUIRE_EQUAL(GetLastJustifiedEpoch(), 4); - BOOST_REQUIRE_EQUAL(GetLastFinalizedEpoch(), 3); + BOOST_REQUIRE_EQUAL(GetCurrentDynasty(), 2); + BOOST_REQUIRE_EQUAL(GetCurrentEpoch(), 4); + BOOST_REQUIRE_EQUAL(GetLastJustifiedEpoch(), 2); + BOOST_REQUIRE_EQUAL(GetLastFinalizedEpoch(), 2); BOOST_REQUIRE_EQUAL(GetActiveFinalizers().size(), 1); + BOOST_REQUIRE_EQUAL(m_expected_source_epoch, 2); + BOOST_REQUIRE_EQUAL(m_recommended_target_epoch, 3); } diff --git a/src/test/esperanza/finalizationstate_utils.h b/src/test/esperanza/finalizationstate_utils.h index 614104a6d3..19ed79a367 100644 --- a/src/test/esperanza/finalizationstate_utils.h +++ b/src/test/esperanza/finalizationstate_utils.h @@ -32,6 +32,9 @@ class FinalizationStateSpy : public FinalizationState { void SetExpectedSourceEpoch(uint32_t epoch) { m_expected_source_epoch = epoch; } + void SetRecommendedTargetEpoch(uint32_t epoch) { + m_recommended_target_epoch = epoch; + } void SetLastFinalizedEpoch(uint32_t epoch) { m_checkpoints[epoch].m_is_finalized = true; m_last_finalized_epoch = epoch; diff --git a/src/test/esperanza/finalizationstate_vote_tests.cpp b/src/test/esperanza/finalizationstate_vote_tests.cpp index cdd12e3542..ef2ea9d263 100644 --- a/src/test/esperanza/finalizationstate_vote_tests.cpp +++ b/src/test/esperanza/finalizationstate_vote_tests.cpp @@ -64,36 +64,20 @@ BOOST_AUTO_TEST_CASE(validate_vote_tx_too_early) { vote = {validatorAddress, targetHash, 0, 1}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_NOT_VOTABLE); - // e3/d0 - try to vote but fail because too early + // e3/d1 - try to vote but fail because too early BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 2 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 3); - BOOST_CHECK_EQUAL(spy.GetCurrentDynasty(), 0); + BOOST_CHECK_EQUAL(spy.GetCurrentDynasty(), 1); vote = {validatorAddress, targetHash, 1, 2}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_NOT_VOTABLE); - // e4/d1 - try to vote but fail because too early + // e4/d2 - try to vote and succeed BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 3 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 4); - BOOST_CHECK_EQUAL(spy.GetCurrentDynasty(), 1); - - vote = {validatorAddress, targetHash, 2, 3}; - BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_NOT_VOTABLE); - - // e5/d2 - try to vote but fail because too early - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 4 * spy.EpochLength()), +Result::SUCCESS); - BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 5); BOOST_CHECK_EQUAL(spy.GetCurrentDynasty(), 2); - vote = {validatorAddress, targetHash, 3, 4}; - BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_NOT_VOTABLE); - - // e6/d3 - try to vote and succeed - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 5 * spy.EpochLength()), +Result::SUCCESS); - BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 6); - BOOST_CHECK_EQUAL(spy.GetCurrentDynasty(), 3); - - vote = {validatorAddress, targetHash, 4, 5}; + vote = {validatorAddress, targetHash, 2, 3}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); } @@ -114,7 +98,7 @@ BOOST_AUTO_TEST_CASE(validate_vote_tx_non_votable_already_voted) { spy.CreateAndActivateDeposit(validatorAddress, depositSize); - Vote vote{validatorAddress, targetHash, 3, 5}; + Vote vote{validatorAddress, targetHash, 2, 3}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); spy.ProcessVote(vote); BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_ALREADY_VOTED); @@ -136,10 +120,10 @@ BOOST_AUTO_TEST_CASE(validate_vote_tx_non_votable_wrong_target_epoch) { spy.CreateAndActivateDeposit(validatorAddress, depositSize); - Vote vote{validatorAddress, targetHash, 3, 4}; + Vote vote{validatorAddress, targetHash, 2, 2}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_WRONG_TARGET_EPOCH); - vote = {validatorAddress, targetHash, 3, 6}; + vote = {validatorAddress, targetHash, 2, 4}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_WRONG_TARGET_EPOCH); } @@ -159,7 +143,7 @@ BOOST_AUTO_TEST_CASE(validate_vote_tx_non_votable_wrong_target_hash) { spy.CreateAndActivateDeposit(validatorAddress, depositSize); - Vote vote{validatorAddress, targetHash, 3, 6}; + Vote vote{validatorAddress, targetHash, 2, 3}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_WRONG_TARGET_HASH); } @@ -178,7 +162,7 @@ BOOST_AUTO_TEST_CASE(validate_vote_tx_non_votable_source_epoch_not_justified) { spy.CreateAndActivateDeposit(validatorAddress, depositSize); - Vote vote{validatorAddress, targetHash, 5, 5}; + Vote vote{validatorAddress, targetHash, 3, 3}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::VOTE_SRC_EPOCH_NOT_JUSTIFIED); } @@ -198,7 +182,7 @@ BOOST_AUTO_TEST_CASE(process_vote_tx_success) { spy.CreateAndActivateDeposit(validatorAddress, depositSize); - Vote vote{validatorAddress, targetHash, 4, 5}; + Vote vote{validatorAddress, targetHash, 2, 3}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); } @@ -228,16 +212,14 @@ BOOST_AUTO_TEST_CASE(process_vote_tx_success_with_reward_no_consensus) { BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 1 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 2 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 3 * spy.EpochLength()), +Result::SUCCESS); - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 4 * spy.EpochLength()), +Result::SUCCESS); - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 5 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.GetActiveFinalizers().size(), 2); - Vote vote{validatorAddress_1, targetHash, 3, 5}; + Vote vote{validatorAddress_1, targetHash, 2, 3}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); spy.ProcessVote(vote); - BOOST_CHECK_EQUAL(spy.Checkpoints()[5].m_is_justified, false); - BOOST_CHECK_EQUAL(spy.Checkpoints()[5].m_is_finalized, false); + BOOST_CHECK_EQUAL(spy.Checkpoints()[3].m_is_justified, false); + BOOST_CHECK_EQUAL(spy.Checkpoints()[3].m_is_finalized, false); } BOOST_AUTO_TEST_CASE(process_vote_tx_success_with_finalization) { @@ -266,29 +248,28 @@ BOOST_AUTO_TEST_CASE(process_vote_tx_success_with_finalization) { BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 1 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 2 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 3 * spy.EpochLength()), +Result::SUCCESS); - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 4 * spy.EpochLength()), +Result::SUCCESS); - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 5 * spy.EpochLength()), +Result::SUCCESS); BOOST_CHECK_EQUAL(spy.GetActiveFinalizers().size(), 2); + BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 4 * spy.EpochLength()), +Result::SUCCESS); - Vote vote{validatorAddress_2, targetHash, 4, 5}; + Vote vote{validatorAddress_2, targetHash, 2, 4}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); spy.ProcessVote(vote); - BOOST_CHECK_EQUAL(spy.Checkpoints()[5].m_is_justified, true); - BOOST_CHECK_EQUAL(spy.Checkpoints()[5].m_is_finalized, false); + BOOST_CHECK_EQUAL(spy.Checkpoints()[4].m_is_justified, true); + BOOST_CHECK_EQUAL(spy.Checkpoints()[4].m_is_finalized, false); - BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 6 * spy.EpochLength()), +Result::SUCCESS); + BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + 5 * spy.EpochLength()), +Result::SUCCESS); targetHash = GetRandHash(); block_index.phashBlock = &targetHash; spy.SetRecommendedTarget(block_index); - vote = {validatorAddress_2, targetHash, 5, 6}; + vote = {validatorAddress_2, targetHash, 4, 5}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); spy.ProcessVote(vote); BOOST_CHECK_EQUAL(spy.Checkpoints()[5].m_is_justified, true); - BOOST_CHECK_EQUAL(spy.Checkpoints()[4].m_is_finalized, true); + BOOST_CHECK_EQUAL(spy.Checkpoints()[5].m_is_finalized, true); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/esperanza/finalizationstate_withdraw_tests.cpp b/src/test/esperanza/finalizationstate_withdraw_tests.cpp index 57f1095d04..829f715608 100644 --- a/src/test/esperanza/finalizationstate_withdraw_tests.cpp +++ b/src/test/esperanza/finalizationstate_withdraw_tests.cpp @@ -48,20 +48,19 @@ BOOST_AUTO_TEST_CASE(process_withdraw_too_early) { // logout BOOST_CHECK_EQUAL(spy.ValidateLogout(validatorAddress), +Result::SUCCESS); spy.ProcessLogout(validatorAddress); - BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 6); + BOOST_CHECK_EQUAL(spy.GetCurrentEpoch(), 4); Validator *validator = &(*spy.pValidators())[validatorAddress]; // Logout delay is set in dynasties but since we have finalization // every epoch, it's equal to number of epochs. - // epoch=706 is the last epoch the finalizer can vote uint32_t end_logout = spy.GetCurrentEpoch() + static_cast(spy.DynastyLogoutDelay()); - BOOST_CHECK_EQUAL(end_logout, 11); + BOOST_CHECK_EQUAL(end_logout, 9); // From epoch end_logout+1 until end_withdraw-1 finalizer can't withdraw. // At end_withdraw or later finalizer can withdraw its deposit. uint32_t end_withdraw = end_logout + static_cast(spy.WithdrawalEpochDelay()) + 1; - BOOST_CHECK_EQUAL(end_withdraw, 22); + BOOST_CHECK_EQUAL(end_withdraw, 20); for (uint32_t i = spy.GetCurrentEpoch(); i < end_withdraw; ++i) { if (spy.GetCurrentDynasty() <= validator->m_end_dynasty) { @@ -75,7 +74,11 @@ BOOST_AUTO_TEST_CASE(process_withdraw_too_early) { BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + i * spy.EpochLength()), +Result::SUCCESS); } - BOOST_CHECK_EQUAL(spy.ValidateWithdraw(validatorAddress, depositSize), +Result::SUCCESS); + // test that deposit can be withdrawn in any upcoming epoch + for (uint32_t i = end_withdraw; i < end_withdraw + 3; ++i) { + BOOST_CHECK_EQUAL(spy.ValidateWithdraw(validatorAddress, depositSize), +Result::SUCCESS); + BOOST_CHECK_EQUAL(spy.InitializeEpoch(1 + i * spy.EpochLength()), +Result::SUCCESS); + } } BOOST_AUTO_TEST_CASE(process_withdraw_completely_slashed) { @@ -107,8 +110,8 @@ BOOST_AUTO_TEST_CASE(process_withdraw_completely_slashed) { // Just to be sure we are after the lock period uint32_t endEpoch = spy.DynastyLogoutDelay() + spy.WithdrawalEpochDelay() + 10; - for (uint32_t i = 6; i < endEpoch; ++i) { - if (spy.GetCurrentDynasty() < validator->m_end_dynasty) { + for (uint32_t i = 4; i < endEpoch; ++i) { + if (spy.GetCurrentDynasty() <= validator->m_end_dynasty) { Vote vote{validatorAddress, targetHash, i - 2, i - 1}; BOOST_CHECK_EQUAL(spy.ValidateVote(vote), +Result::SUCCESS); diff --git a/src/test/finalization/state_processor_tests.cpp b/src/test/finalization/state_processor_tests.cpp index add0426cab..02bf40fcf0 100644 --- a/src/test/finalization/state_processor_tests.cpp +++ b/src/test/finalization/state_processor_tests.cpp @@ -111,14 +111,14 @@ BOOST_AUTO_TEST_CASE(trimming) { fixture.AddBlocks(1); // Generate first two epochs - fixture.AddBlocks(10); + fixture.AddBlocks(5); // Check, all states presented in the repository - for (blockchain::Height i = 0; i < 10; ++i) { + for (blockchain::Height i = 0; i < 5; ++i) { BOOST_CHECK(fixture.GetState(i) != nullptr); } // Check, states are different - for (blockchain::Height h1 = 0; h1 < 10; ++h1) { + for (blockchain::Height h1 = 0; h1 < 5; ++h1) { for (blockchain::Height h2 = 0; h2 <= h1; ++h2) { const auto lhs = fixture.GetState(h1); const auto rhs = fixture.GetState(h2); @@ -137,18 +137,28 @@ BOOST_AUTO_TEST_CASE(trimming) { BOOST_CHECK(fixture.GetState(2) == nullptr); BOOST_CHECK(fixture.GetState(3) == nullptr); BOOST_CHECK(fixture.GetState(4) == nullptr); - BOOST_CHECK(fixture.GetState(5) == nullptr); // finalized checkpoint - BOOST_CHECK(fixture.GetState(6) == nullptr); - BOOST_CHECK(fixture.GetState(7) == nullptr); - BOOST_CHECK(fixture.GetState(8) == nullptr); - BOOST_CHECK(fixture.GetState(9) == nullptr); - BOOST_CHECK(fixture.GetState(10) != nullptr); // justified checkpoint - BOOST_CHECK(fixture.GetState(11) != nullptr); // next epoch + BOOST_CHECK(fixture.GetState(5) != nullptr); // finalized checkpoint + BOOST_CHECK(fixture.GetState(6) != nullptr); // next epoch // Complete current epoch fixture.AddBlocks(4); // Check, new states are in the repository + BOOST_CHECK(fixture.GetState(5) != nullptr); + BOOST_CHECK(fixture.GetState(6) != nullptr); + BOOST_CHECK(fixture.GetState(7) != nullptr); + BOOST_CHECK(fixture.GetState(8) != nullptr); + BOOST_CHECK(fixture.GetState(9) != nullptr); + BOOST_CHECK(fixture.GetState(10) != nullptr); + + // Generate next epoch. + // Now epoch 2 must be finalized and repository trimmed until the last justification height + fixture.AddBlocks(5); + BOOST_CHECK(fixture.GetState(5) == nullptr); + BOOST_CHECK(fixture.GetState(6) == nullptr); + BOOST_CHECK(fixture.GetState(7) == nullptr); + BOOST_CHECK(fixture.GetState(8) == nullptr); + BOOST_CHECK(fixture.GetState(9) == nullptr); BOOST_CHECK(fixture.GetState(10) != nullptr); BOOST_CHECK(fixture.GetState(11) != nullptr); BOOST_CHECK(fixture.GetState(12) != nullptr); @@ -156,9 +166,8 @@ BOOST_AUTO_TEST_CASE(trimming) { BOOST_CHECK(fixture.GetState(14) != nullptr); BOOST_CHECK(fixture.GetState(15) != nullptr); - // Generate next epoch. - // Now epoch 1 must be finalized and repository trimmed until the last justification height - fixture.AddBlocks(5); + // Generate one more block, trigger finalization of the epoch 3 + fixture.AddBlocks(1); BOOST_CHECK(fixture.GetState(10) == nullptr); BOOST_CHECK(fixture.GetState(11) == nullptr); BOOST_CHECK(fixture.GetState(12) == nullptr); @@ -166,20 +175,6 @@ BOOST_AUTO_TEST_CASE(trimming) { BOOST_CHECK(fixture.GetState(14) == nullptr); BOOST_CHECK(fixture.GetState(15) != nullptr); BOOST_CHECK(fixture.GetState(16) != nullptr); - BOOST_CHECK(fixture.GetState(17) != nullptr); - BOOST_CHECK(fixture.GetState(18) != nullptr); - BOOST_CHECK(fixture.GetState(19) != nullptr); - BOOST_CHECK(fixture.GetState(20) != nullptr); - - // Generate one more block, trigger finalization of the epoch 2. - fixture.AddBlocks(1); - BOOST_CHECK(fixture.GetState(15) == nullptr); - BOOST_CHECK(fixture.GetState(16) == nullptr); - BOOST_CHECK(fixture.GetState(17) == nullptr); - BOOST_CHECK(fixture.GetState(18) == nullptr); - BOOST_CHECK(fixture.GetState(19) == nullptr); - BOOST_CHECK(fixture.GetState(20) != nullptr); - BOOST_CHECK(fixture.GetState(21) != nullptr); } BOOST_AUTO_TEST_CASE(states_workflow) { @@ -190,7 +185,7 @@ BOOST_AUTO_TEST_CASE(states_workflow) { fixture.AddBlocks(1); // Generate first epoch - fixture.AddBlocks(10); + fixture.AddBlocks(5); bool ok = false; auto &block_index = fixture.CreateBlockIndex(); diff --git a/src/test/finalization/state_repository_tests.cpp b/src/test/finalization/state_repository_tests.cpp index 606a777cd0..ff7b637f2b 100644 --- a/src/test/finalization/state_repository_tests.cpp +++ b/src/test/finalization/state_repository_tests.cpp @@ -237,7 +237,7 @@ BOOST_AUTO_TEST_CASE(recovering) { BOOST_REQUIRE(fixture.m_state_db.m_states.count(index) == 0); }; - for (size_t i = 0; i < 10; ++i) { + for (size_t i = 0; i < 5; ++i) { const CBlockIndex &index = fixture.CreateBlockIndex(); finalization::FinalizationState *state = fixture.m_repo->FindOrCreate(index, S::NEW); BOOST_REQUIRE(state != nullptr); @@ -264,91 +264,91 @@ BOOST_AUTO_TEST_CASE(recovering) { // Remove one state from DB and check how it's restored. { - remove_from_db(5); + remove_from_db(2); fixture.m_block_db.blocks.clear(); - fixture.m_block_db.blocks[fixture.m_chain.AtHeight(5)] = {}; + fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)] = {}; auto restored_repo = fixture.NewRepo(); auto proc = finalization::StateProcessor::New( &fixture.m_finalization_params, restored_repo.get(), &fixture.m_chain); restored_repo->RestoreFromDisk(proc.get()); - BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(5)].read_requests, 1); + BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)].read_requests, 1); LOCK(restored_repo->GetLock()); - BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(5)) != nullptr); + BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(2)) != nullptr); check_restored(*restored_repo); } // Remove second state from DB and check how it's restored. { - remove_from_db(4); + remove_from_db(3); fixture.m_block_db.blocks.clear(); - fixture.m_block_db.blocks[fixture.m_chain.AtHeight(4)] = {}; - fixture.m_block_db.blocks[fixture.m_chain.AtHeight(5)] = {}; + fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)] = {}; + fixture.m_block_db.blocks[fixture.m_chain.AtHeight(3)] = {}; auto restored_repo = fixture.NewRepo(); auto proc = finalization::StateProcessor::New( &fixture.m_finalization_params, restored_repo.get(), &fixture.m_chain); restored_repo->RestoreFromDisk(proc.get()); - BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(4)].read_requests, 1); - BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(5)].read_requests, 1); + BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)].read_requests, 1); + BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(3)].read_requests, 1); LOCK(restored_repo->GetLock()); - BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(4)) != nullptr); - BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(5)) != nullptr); + BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(2)) != nullptr); + BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(3)) != nullptr); check_restored(*restored_repo); } // Remove tip's state from DB and check how it's restored. { - remove_from_db(9); + remove_from_db(4); fixture.m_block_db.blocks.clear(); + fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)] = {}; + fixture.m_block_db.blocks[fixture.m_chain.AtHeight(3)] = {}; fixture.m_block_db.blocks[fixture.m_chain.AtHeight(4)] = {}; - fixture.m_block_db.blocks[fixture.m_chain.AtHeight(5)] = {}; - fixture.m_block_db.blocks[fixture.m_chain.AtHeight(9)] = {}; auto restored_repo = fixture.NewRepo(); auto proc = finalization::StateProcessor::New( &fixture.m_finalization_params, restored_repo.get(), &fixture.m_chain); restored_repo->RestoreFromDisk(proc.get()); + BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)].read_requests, 1); + BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(3)].read_requests, 1); BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(4)].read_requests, 1); - BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(5)].read_requests, 1); - BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(9)].read_requests, 1); LOCK(restored_repo->GetLock()); + BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(2)) != nullptr); + BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(3)) != nullptr); BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(4)) != nullptr); - BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(5)) != nullptr); - BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(9)) != nullptr); check_restored(*restored_repo); } // Remove tip's state from DB and check how it's restored (backward ordering in BlockIndexMap.ForEach). { fixture.m_block_db.blocks.clear(); + fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)] = {}; + fixture.m_block_db.blocks[fixture.m_chain.AtHeight(3)] = {}; fixture.m_block_db.blocks[fixture.m_chain.AtHeight(4)] = {}; - fixture.m_block_db.blocks[fixture.m_chain.AtHeight(5)] = {}; - fixture.m_block_db.blocks[fixture.m_chain.AtHeight(9)] = {}; fixture.m_block_indexes.reverse = true; auto restored_repo = fixture.NewRepo(); auto proc = finalization::StateProcessor::New( &fixture.m_finalization_params, restored_repo.get(), &fixture.m_chain); restored_repo->RestoreFromDisk(proc.get()); + BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)].read_requests, 1); + BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(3)].read_requests, 1); BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(4)].read_requests, 1); - BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(5)].read_requests, 1); - BOOST_CHECK_EQUAL(fixture.m_block_db.blocks[fixture.m_chain.AtHeight(9)].read_requests, 1); LOCK(restored_repo->GetLock()); + BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(2)) != nullptr); + BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(3)) != nullptr); BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(4)) != nullptr); - BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(5)) != nullptr); - BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(9)) != nullptr); check_restored(*restored_repo); } - // Remove block 5 from the disk! + // Remove block 3 from the disk! { fixture.m_block_db.blocks.clear(); + fixture.m_block_db.blocks[fixture.m_chain.AtHeight(2)] = {}; fixture.m_block_db.blocks[fixture.m_chain.AtHeight(4)] = {}; - fixture.m_block_db.blocks[fixture.m_chain.AtHeight(9)] = {}; auto restored_repo = fixture.NewRepo(); auto proc = finalization::StateProcessor::New( &fixture.m_finalization_params, restored_repo.get(), &fixture.m_chain); try { restored_repo->RestoreFromDisk(proc.get()); } catch (finalization::MissedBlockError &e) { - BOOST_CHECK(&e.missed_index == fixture.m_chain.AtHeight(5)); + BOOST_CHECK(&e.missed_index == fixture.m_chain.AtHeight(3)); return; } BOOST_REQUIRE(not("unreachable")); @@ -356,7 +356,7 @@ BOOST_AUTO_TEST_CASE(recovering) { // Remove one state from DB and check how it's restored from CBlockIndex { - remove_from_db(5); + remove_from_db(4); fixture.m_block_db.blocks.clear(); auto restored_repo = fixture.NewRepo(); auto proc = finalization::StateProcessor::New( @@ -366,7 +366,7 @@ BOOST_AUTO_TEST_CASE(recovering) { restored_repo->RestoreFromDisk(proc.get()); BOOST_CHECK(fixture.m_block_db.blocks.empty()); LOCK(restored_repo->GetLock()); - BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(5)) != nullptr); + BOOST_CHECK(restored_repo->Find(*fixture.m_chain.AtHeight(4)) != nullptr); check_restored(*restored_repo); } diff --git a/src/validation.cpp b/src/validation.cpp index ee31f35cdd..94ffaecc1b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4475,7 +4475,7 @@ bool IsForkingBeforeLastFinalization(const CBlockIndex &block_index) { return false; } - const uint32_t checkpoint_height = tip_state->GetCheckpointHeightAfterFinalizedEpoch(); + const uint32_t checkpoint_height = tip_state->GetEpochCheckpointHeight(tip_state->GetLastFinalizedEpoch()); const CBlockIndex *checkpoint = chainActive[checkpoint_height]; assert(checkpoint); @@ -4487,26 +4487,6 @@ bool IsForkingBeforeLastFinalization(const CBlockIndex &block_index) { return true; } - // we know that we have finalized epoch on this fork and - // it is at least justified. We need to check that it is also finalized - uint32_t justified_epoch = tip_state->GetLastFinalizedEpoch() + 1; - uint32_t next_checkpoint_height = tip_state->GetEpochCheckpointHeight(justified_epoch + 1); - - // this block must have votes to justify the epoch - // which follows after the finalized one on chainActive - uint32_t height = std::min(next_checkpoint_height, static_cast(block_index.nHeight)); - const CBlockIndex *cp_fork = block_index.GetAncestor(height); - assert(cp_fork); - esperanza::FinalizationState *cp_fork_state = state_repo->Find(*cp_fork); - assert(cp_fork_state); - - if (cp_fork_state->GetLastJustifiedEpoch() != justified_epoch) { - // this fork doesn't justify the expected epoch - return true; - } - - // sanity check - assert(cp_fork_state->GetLastFinalizedEpoch() == tip_state->GetLastFinalizedEpoch()); return false; } diff --git a/test/functional/feature_commits_forkchoice.py b/test/functional/feature_commits_forkchoice.py index f52b70a17e..6bc9a85440 100755 --- a/test/functional/feature_commits_forkchoice.py +++ b/test/functional/feature_commits_forkchoice.py @@ -83,7 +83,7 @@ def run_test(self): assert_finalizationstate(p0, {'currentEpoch': 5, 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3}) + 'lastFinalizedEpoch': 4}) # disconnect p0 # v0: p1, p2 @@ -108,28 +108,27 @@ def run_test(self): disconnect_nodes(p1, v0.index) # generate long chain in p0 but don't justify it - # F J + # F F # 30 .. 40 .. 89 -- p0 generate_block(p0, count=40) assert_equal(p0.getblockcount(), 89) assert_finalizationstate(p0, {'currentEpoch': 9, 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3}) + 'lastFinalizedEpoch': 4}) # generate short chain in p1 and justify it # on the 6th and 7th epochs sync with validator - # F J + # F F # 30 .. 40 .. 49 .. .. .. .. .. .. 89 -- p0 # \ # 50 .. 60 .. 69 -- p1 - # F J + # F # get to the 6th epoch generate_block(p1, count=2) self.wait_for_vote_and_disconnect(finalizer=v0, node=p1) # get to the 7th epoch generate_block(p1, count=10) - self.wait_for_vote_and_disconnect(finalizer=v0, node=p1) # generate the rest of the blocks generate_block(p1, count=8) connect_nodes(p1, v0.index) @@ -137,7 +136,7 @@ def run_test(self): assert_equal(p1.getblockcount(), 69) assert_finalizationstate(p1, {'currentEpoch': 7, - 'lastJustifiedEpoch': 6, + 'lastJustifiedEpoch': 5, 'lastFinalizedEpoch': 5}) # connect p2 with p0 and p1; p2 must switch to the longest justified p1 @@ -154,10 +153,10 @@ def run_test(self): assert_equal(p2.getblockcount(), 69) assert_finalizationstate(p1, {'currentEpoch': 7, - 'lastJustifiedEpoch': 6, + 'lastJustifiedEpoch': 5, 'lastFinalizedEpoch': 5}) assert_finalizationstate(p2, {'currentEpoch': 7, - 'lastJustifiedEpoch': 6, + 'lastJustifiedEpoch': 5, 'lastFinalizedEpoch': 5}) # connect p0 with p1, p0 must disconnect its longest but not justified fork and choose p1 diff --git a/test/functional/feature_finalizer.py b/test/functional/feature_finalizer.py index b2e02428dd..e118d2108a 100755 --- a/test/functional/feature_finalizer.py +++ b/test/functional/feature_finalizer.py @@ -47,22 +47,22 @@ def run_test(self): self.restart_node(v.index) self.log.info("Leave insta justification") - for _ in range(24): + for _ in range(14): generate_block(p) - assert_equal(p.getblockcount(), 26) - assert_finalizationstate(p, {"currentEpoch": 6, - "lastJustifiedEpoch": 4, - "lastFinalizedEpoch": 3, + assert_equal(p.getblockcount(), 16) + assert_finalizationstate(p, {"currentEpoch": 4, + "lastJustifiedEpoch": 2, + "lastFinalizedEpoch": 2, "validators": 1}) self.log.info("Check finalizer votes after restart") self.wait_for_vote_and_disconnect(finalizer=v, node=p) generate_block(p) - assert_equal(p.getblockcount(), 27) - assert_finalizationstate(p, {"currentEpoch": 6, - "lastJustifiedEpoch": 5, - "lastFinalizedEpoch": 4, + assert_equal(p.getblockcount(), 17) + assert_finalizationstate(p, {"currentEpoch": 4, + "lastJustifiedEpoch": 3, + "lastFinalizedEpoch": 3, "validators": 1}) diff --git a/test/functional/feature_fork_choice_finalization.py b/test/functional/feature_fork_choice_finalization.py index e7c3f722d4..c6e0478d7c 100755 --- a/test/functional/feature_fork_choice_finalization.py +++ b/test/functional/feature_fork_choice_finalization.py @@ -95,14 +95,14 @@ def connect_sync_disconnect(node1, node2, blockhash): disconnect_nodes(node0, validator.index) assert_equal(len(node0.getpeerinfo()), 0) - # F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] - generate_block(node0, count=25) - assert_equal(node0.getblockcount(), 26) - assert_finalizationstate(node0, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + # F F F + # e0 - e1 - e2 - e3 - e4[16] + generate_block(node0, count=15) + assert_equal(node0.getblockcount(), 16) + assert_finalizationstate(node0, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 1}) connect_nodes(node0, node1.index) @@ -112,51 +112,51 @@ def connect_sync_disconnect(node1, node2, blockhash): disconnect_nodes(node0, node2.index) # generate fork with no commits. node0 must switch to it - # 26 node1 + # 16 node1 # \ - # - b27 node0, node2 - b27 = generate_block(node2)[-1] - connect_sync_disconnect(node0, node2, b27) - assert_equal(node0.getblockcount(), 27) + # - b17 node0, node2 + b17 = generate_block(node2)[-1] + connect_sync_disconnect(node0, node2, b17) + assert_equal(node0.getblockcount(), 17) # generate fork with justified commits. node0 must switch to it - # - 27 - b28 node0, node1 + # - 17 - b18 node0, node1 # / - # 26 + # 16 # \ - # - b27 node2 + # - b17 node2 self.wait_for_vote_and_disconnect(finalizer=validator, node=node1) - b28 = generate_block(node1, count=2)[-1] - connect_sync_disconnect(node0, node1, b28) - assert_equal(node0.getblockcount(), 28) - assert_finalizationstate(node0, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4, + b18 = generate_block(node1, count=2)[-1] + connect_sync_disconnect(node0, node1, b18) + assert_equal(node0.getblockcount(), 18) + assert_finalizationstate(node0, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3, 'validators': 1}) self.log.info('node successfully switched to longest justified fork') # generate longer but not justified fork. node0 shouldn't switch - # - 27 - b28 node0, node1, node2 + # - 17 - b18 node0, node1, node2 # / - # 26 + # 16 # \ - # - 27 - 28 - 29 - b30 - generate_block(node2, count=3)[-1] # b30 - assert_equal(node2.getblockcount(), 30) - assert_equal(node0.getblockcount(), 28) + # - 17 - 18 - 19 - b20 + generate_block(node2, count=3)[-1] # b20 + assert_equal(node2.getblockcount(), 20) + assert_equal(node0.getblockcount(), 18) connect_nodes(node0, node2.index) sync_blocks([node0, node2], timeout=10) - assert_equal(node0.getblockcount(), 28) - assert_equal(node0.getblockhash(28), b28) - assert_equal(node0.getfinalizationstate()['lastJustifiedEpoch'], 5) + assert_equal(node0.getblockcount(), 18) + assert_equal(node0.getblockhash(18), b18) + assert_equal(node0.getfinalizationstate()['lastJustifiedEpoch'], 3) self.log.info('node did not switch to heaviest but less justified fork') - assert_equal(node2.getblockcount(), 28) - assert_equal(node2.getblockhash(28), b28) - assert_equal(node2.getfinalizationstate()['lastJustifiedEpoch'], 5) + assert_equal(node2.getblockcount(), 18) + assert_equal(node2.getblockhash(18), b18) + assert_equal(node2.getfinalizationstate()['lastJustifiedEpoch'], 3) self.log.info('node switched to longest justified fork with less work') self.stop_node(node0.index) @@ -192,33 +192,33 @@ def test_heaviest_justified_epoch(self): disconnect_nodes(fork1, finalizer.index) # leave instant justification - # F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] - generate_block(fork1, count=3 + 5 + 5 + 5 + 5 + 1) - assert_equal(fork1.getblockcount(), 26) - assert_finalizationstate(fork1, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + # F F F + # e0 - e1 - e2 - e3 - e4[16] + generate_block(fork1, count=3 + 5 + 5 + 1) + assert_equal(fork1.getblockcount(), 16) + assert_finalizationstate(fork1, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 1}) - # justify epoch=5 - # J - # e5 - e6 fork1, fork2, fork3 + # finalize epoch=3 + # F + # e3 - e4 fork1, fork2, fork3 self.wait_for_vote_and_disconnect(finalizer=finalizer, node=fork1) generate_block(fork1, count=4) - assert_equal(fork1.getblockcount(), 30) - assert_finalizationstate(fork1, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4}) + assert_equal(fork1.getblockcount(), 20) + assert_finalizationstate(fork1, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3}) - # create two forks at epoch=6 that use the same votes to justify epoch=5 + # create two forks at epoch=4 that use the same votes to justify epoch=3 # fork3 - # F J | - # e5 - e6[.., 30] - e7[31, 32] fork1 + # F F | + # e3 - e4[.., 20] - e5[21, 22] fork1 # \ - # - 32, 33] fork2 + # - 22, 23] fork2 sync_blocks([fork1, fork3], timeout=10) disconnect_nodes(fork1, fork3.index) generate_block(fork1) @@ -227,65 +227,66 @@ def test_heaviest_justified_epoch(self): self.wait_for_vote_and_disconnect(finalizer=finalizer, node=fork1) for fork in [fork1, fork2]: wait_until(lambda: len(fork.getrawmempool()) == 1, timeout=10) - assert_equal(fork.getblockcount(), 31) - assert_finalizationstate(fork, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4}) + assert_equal(fork.getblockcount(), 21) + assert_finalizationstate(fork, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3}) disconnect_nodes(fork1, fork2.index) vote = fork1.getrawtransaction(fork1.getrawmempool()[0]) for fork in [fork1, fork2]: generate_block(fork) - assert_equal(fork.getblockcount(), 32) - assert_finalizationstate(fork, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + assert_equal(fork.getblockcount(), 22) + assert_finalizationstate(fork, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) - b33 = generate_block(fork2)[0] + b23 = generate_block(fork2)[0] # test that fork1 switches to the heaviest fork # fork3 - # F J | - # e5 - e6[.., 30] - e7[31, 32] - # \ - # - 32, 33] fork2, fork1 + # F F | + # e3 - e4[.., 20] - e5[21, 22] + # \ v + # - 22, 23] fork2, fork1 connect_nodes(fork1, fork2.index) - fork1.waitforblock(b33) + fork1.waitforblock(b23) - assert_equal(fork1.getblockcount(), 33) - assert_equal(fork1.getblockhash(33), b33) - assert_finalizationstate(fork1, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + assert_equal(fork1.getblockcount(), 23) + assert_equal(fork1.getblockhash(23), b23) + assert_finalizationstate(fork1, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) disconnect_nodes(fork1, fork2.index) # test that fork1 switches to the heaviest fork - # - e7[31, 32, 33, 34, 35] fork3, fork1 - # F J / - # e5 - e6[.., 30] - e7[31, 32] - # \ - # - 32, 33] fork2 - assert_equal(fork3.getblockcount(), 30) + # v + # - e5[21, 22, 23, 24, 25] fork3, fork1 + # F F / + # e3 - e4[.., 20] - e5[21, 22] + # \ v + # - 22, 23] fork2 + assert_equal(fork3.getblockcount(), 20) generate_block(fork3, count=4) fork3.sendrawtransaction(vote) wait_until(lambda: len(fork3.getrawmempool()) == 1, timeout=10) - b35 = generate_block(fork3)[0] - assert_equal(fork3.getblockcount(), 35) + b25 = generate_block(fork3)[0] + assert_equal(fork3.getblockcount(), 25) connect_nodes(fork1, fork3.index) - fork1.waitforblock(b35) - - assert_equal(fork1.getblockcount(), 35) - assert_equal(fork1.getblockhash(35), b35) - assert_finalizationstate(fork1, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + fork1.waitforblock(b25) + + assert_equal(fork1.getblockcount(), 25) + assert_equal(fork1.getblockhash(25), b25) + assert_finalizationstate(fork1, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) self.stop_node(fork1.index) self.stop_node(fork2.index) diff --git a/test/functional/feature_fork_choice_forked_finalize_epoch.py b/test/functional/feature_fork_choice_forked_finalize_epoch.py index 4111d787d5..67e08ab643 100755 --- a/test/functional/feature_fork_choice_forked_finalize_epoch.py +++ b/test/functional/feature_fork_choice_forked_finalize_epoch.py @@ -8,21 +8,21 @@ on another fork Scenario 1: fork after finalized checkpoint -Node shouldn't switch to the fork, even if it has longer justified channel -because epoch=5 is not finalized for that fork - F J -[ e5 ] - [ e6 ] - [ e7 ] node +Node shouldn't switch to the fork, even if it has longer justified chain +because epoch=3 is not finalized for that fork + F F +[ e3 ] - [ e4 ] - [ e5 ] node | | J - ..] - [ e6 ] - [ e7 ] - [ e8 ] fork + ..] - [ e4 ] - [ e5 ] - [ e6 ] fork Scenario 2: fork after justified checkpoint -Node shouldn't switch to the fork because its epoch=5 is not finalized - F F J -... [ e4 ] - [ e5 ] - [ e6 ] - [ e7 ] node +Node shouldn't switch to the fork because its epoch=4 is not finalized + F F F +... [ e2 ] - [ e3 ] - [ e4 ] - [ e5 ] node | | J - .. ] - [ e7 ] - [ e8 ] - [ e9 ] fork + .. ] - [ e5 ] - [ e6 ] - [ e6 ] fork """ from test_framework.test_framework import UnitETestFramework @@ -83,83 +83,83 @@ def test_fork_on_finalized_checkpoint(self): disconnect_nodes(node, finalizer.index) # leave instant justification - generate_block(node, count=3 + 5 + 5 + 5 + 5) + generate_block(node, count=3 + 5 + 5) sync_blocks([node, fork], timeout=10) - assert_equal(node.getblockcount(), 25) - assert_finalizationstate(node, {'currentDynasty': 2, - 'currentEpoch': 5, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + assert_equal(node.getblockcount(), 15) + assert_finalizationstate(node, {'currentDynasty': 1, + 'currentEpoch': 3, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 0}) # create longer justified fork - # [ e5 ] node + # [ e3 ] node # | # | J - # ..] - [ e6 ] - [ e7 ] - [ e8 ] fork + # ..] - [ e4 ] - [ e5 ] - [ e6 ] fork disconnect_nodes(node, fork.index) generate_block(fork, count=5 + 5) target = fork.getbestblockhash() generate_block(fork) vtx = make_vote_tx(finalizer, finalizer_address, target, - source_epoch=4, target_epoch=7, input_tx_id=deposit_tx_id) + source_epoch=2, target_epoch=5, input_tx_id=deposit_tx_id) fork.sendrawtransaction(vtx) generate_block(fork) - assert_equal(fork.getblockcount(), 37) - assert_finalizationstate(fork, {'currentDynasty': 3, - 'currentEpoch': 8, - 'lastJustifiedEpoch': 7, - 'lastFinalizedEpoch': 3}) + assert_equal(fork.getblockcount(), 27) + assert_finalizationstate(fork, {'currentDynasty': 2, + 'currentEpoch': 6, + 'lastJustifiedEpoch': 5, + 'lastFinalizedEpoch': 2}) # create finalization - # J - # [ e5 ] - [ e6 ] node + # F + # [ e3 ] - [ e4 ] node # | # | J - # ..] - [ e6 ] - [ e7 ] - [ e8 ] fork + # ..] - [ e4 ] - [ e5 ] - [ e6 ] fork generate_block(node) self.wait_for_vote_and_disconnect(finalizer=finalizer, node=node) generate_block(node, count=4) - assert_equal(node.getblockcount(), 30) - assert_finalizationstate(node, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4}) + assert_equal(node.getblockcount(), 20) + assert_finalizationstate(node, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3}) - # F J - # [ e5 ] - [ e6 ] - [ e7 ] node + # F F + # [ e3 ] - [ e4 ] - [ e5 ] node # | # | J - # ..] - [ e6 ] - [ e7 ] - [ e8 ] fork + # ..] - [ e4 ] - [ e5 ] - [ e6 ] fork generate_block(node) self.wait_for_vote_and_disconnect(finalizer=finalizer, node=node) generate_block(node, count=4) - assert_equal(node.getblockcount(), 35) - assert_finalizationstate(node, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + assert_equal(node.getblockcount(), 25) + assert_finalizationstate(node, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) # test that longer justification doesn't trigger re-org before finalization connect_nodes(node, fork.index) time.sleep(5) # give enough time to decide - assert_equal(node.getblockcount(), 35) - assert_finalizationstate(node, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + assert_equal(node.getblockcount(), 25) + assert_finalizationstate(node, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) # TODO: UNIT-E: check that slash transaction was created # related issue: #680 #652 #686 # test that node has valid state after restart self.restart_node(node.index) - assert_equal(node.getblockcount(), 35) - assert_finalizationstate(node, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + assert_equal(node.getblockcount(), 25) + assert_finalizationstate(node, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) # cleanup self.stop_node(node.index) @@ -191,97 +191,97 @@ def test_fork_on_justified_epoch(self): disconnect_nodes(node, finalizer.index) # leave instant justification - # F F F F J - # [ e0 ] - [ e1 ] - [ e2 ] - [ e3 ] - [ e4 ] - [ e5 ] - generate_block(node, count=3 + 5 + 5 + 5 + 5) + # F F + # [ e0 ] - [ e1 ] - [ e2 ] - [ e3 ] + generate_block(node, count=3 + 5 + 5) sync_blocks([node, fork]) - assert_equal(node.getblockcount(), 25) - assert_finalizationstate(node, {'currentDynasty': 2, - 'currentEpoch': 5, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + assert_equal(node.getblockcount(), 15) + assert_finalizationstate(node, {'currentDynasty': 1, + 'currentEpoch': 3, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 0}) # justify epoch that will be finalized - # F J - # ... [ e4 ] - [ e5 ] - [ e6 ] node, fork + # F F + # ... [ e2 ] - [ e3 ] - [ e4 ] node, fork generate_block(node) vote = self.wait_for_vote_and_disconnect(finalizer=finalizer, node=node) vote_tx_id = node.decoderawtransaction(vote)['txid'] generate_block(node, count=4) sync_blocks([node, fork], timeout=10) - assert_equal(node.getblockcount(), 30) - assert_finalizationstate(node, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4}) + assert_equal(node.getblockcount(), 20) + assert_finalizationstate(node, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3}) # create fork that will be longer justified - # F J - # ... [ e4 ] - [ e5 ] - [ e6 ] node + # F F + # ... [ e2 ] - [ e3 ] - [ e4 ] node # | # | - # .. ] - [ e7 ] - [ e8 ] fork + # .. ] - [ e5 ] - [ e6 ] fork disconnect_nodes(node, fork.index) generate_block(fork, count=5 + 5) - assert_equal(fork.getblockcount(), 40) - assert_finalizationstate(fork, {'currentDynasty': 4, - 'currentEpoch': 8, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4}) + assert_equal(fork.getblockcount(), 30) + assert_finalizationstate(fork, {'currentDynasty': 3, + 'currentEpoch': 6, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3}) # create longer justification - # F J - # ... [ e4 ] - [ e5 ] - [ e6 ] node + # F F + # ... [ e2 ] - [ e3 ] - [ e4 ] node # | # | J - # .. ] - [ e7 ] - [ e8 ] - [ e9 ] fork + # .. ] - [ e5 ] - [ e6 ] - [ e7 ] fork target = fork.getbestblockhash() generate_block(fork) vtx = make_vote_tx(finalizer, finalizer_address, target, - source_epoch=5, target_epoch=8, input_tx_id=vote_tx_id) + source_epoch=3, target_epoch=6, input_tx_id=vote_tx_id) fork.sendrawtransaction(vtx) generate_block(fork, count=4) - assert_equal(fork.getblockcount(), 45) - assert_finalizationstate(fork, {'currentDynasty': 4, - 'currentEpoch': 9, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 4}) + assert_equal(fork.getblockcount(), 35) + assert_finalizationstate(fork, {'currentDynasty': 3, + 'currentEpoch': 7, + 'lastJustifiedEpoch': 6, + 'lastFinalizedEpoch': 3}) # finalize epoch=5 on node - # F F J - # ... [ e4 ] - [ e5 ] - [ e6 ] - [ e7 ] node + # F F F + # ... [ e2 ] - [ e3 ] - [ e4 ] - [ e5 ] node # | # | J - # .. ] - [ e7 ] - [ e8 ] - [ e9 ] fork + # .. ] - [ e5 ] - [ e6 ] - [ e7 ] fork generate_block(node) self.wait_for_vote_and_disconnect(finalizer=finalizer, node=node) generate_block(node) - assert_equal(node.getblockcount(), 32) - assert_finalizationstate(node, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + assert_equal(node.getblockcount(), 22) + assert_finalizationstate(node, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) # node shouldn't switch to fork as it's finalization is behind connect_nodes(node, fork.index) time.sleep(5) - assert_equal(node.getblockcount(), 32) - assert_finalizationstate(node, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + assert_equal(node.getblockcount(), 22) + assert_finalizationstate(node, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) # TODO: UNIT-E: check that slash transaction was created # related issue: #680 #652 #686 # test that node has valid state after restart self.restart_node(node.index) - assert_equal(node.getblockcount(), 32) - assert_finalizationstate(node, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5}) + assert_equal(node.getblockcount(), 22) + assert_finalizationstate(node, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4}) # cleanup self.stop_node(node.index) diff --git a/test/functional/feature_fork_choice_parallel_justifications.py b/test/functional/feature_fork_choice_parallel_justifications.py index 58d48ce464..d22b39577f 100755 --- a/test/functional/feature_fork_choice_parallel_justifications.py +++ b/test/functional/feature_fork_choice_parallel_justifications.py @@ -105,18 +105,18 @@ def generate_epoch_and_vote(node, finalizer, finalizer_address, prevtx): self.wait_for_transaction(deptx) # leave insta justification - # - fork1 - # F F F F J | - # e0 - e1 - e2 - e3 - e4 - e5 - node - # | - # - fork2 - generate_block(node, count=24) - assert_equal(node.getblockcount(), 25) + # - fork1 + # F F F | + # e0 - e1 - e2 - e3 - node + # | + # - fork2 + generate_block(node, count=14) + assert_equal(node.getblockcount(), 15) sync_blocks([node, finalizer]) - assert_finalizationstate(node, {'currentDynasty': 2, - 'currentEpoch': 5, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + assert_finalizationstate(node, {'currentDynasty': 1, + 'currentEpoch': 3, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 0}) sync_blocks(self.nodes) disconnect_nodes(node, fork1.index) @@ -124,105 +124,104 @@ def generate_epoch_and_vote(node, finalizer, finalizer_address, prevtx): disconnect_nodes(node, finalizer.index) # create first justified epoch on fork1 - # J - # - e6 - e7 - e8 fork1 node - # F F F F J | - # e0 - e1 - e2 - e3 - e4 - e5 - - # | - # - fork2 + # J + # - e4 - e5 - e6 fork1 node + # F F F | + # e0 - e1 - e2 - e3 - + # | + # - fork2 generate_block(fork1, count=5) vtx1 = generate_epoch_and_vote(fork1, finalizer, finalizer_address, deptx) generate_block(fork1, count=5) - assert_equal(fork1.getblockcount(), 40) - assert_finalizationstate(fork1, {'currentDynasty': 3, - 'currentEpoch': 8, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 3, + assert_equal(fork1.getblockcount(), 30) + assert_finalizationstate(fork1, {'currentDynasty': 2, + 'currentEpoch': 6, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 2, 'validators': 1}) sync_node_to_fork(node, fork1) - assert_finalizationstate(node, {'currentDynasty': 3, - 'currentEpoch': 8, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 3, + assert_finalizationstate(node, {'currentDynasty': 2, + 'currentEpoch': 6, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 2, 'validators': 1}) self.log.info('node successfully switched to the justified fork') # create longer justified epoch on fork2 # node must switch ("zig") to this fork - # J - # - e6 - e7 - e8 fork1 - # F F F F J | - # e0 - e1 - e2 - e3 - e4 - e5 - - # | J - # - e6 - e7 - e8 fork2 node + # J + # - e4 - e5 - e6 fork1 + # F F F | + # e0 - e1 - e2 - e3 - + # | J + # - e4 - e5 - e6 fork2 node generate_block(fork2, count=10) vtx2 = generate_epoch_and_vote(fork2, finalizer, finalizer_address, deptx) - assert_equal(fork2.getblockcount(), 40) - assert_finalizationstate(fork2, {'currentDynasty': 3, - 'currentEpoch': 8, - 'lastJustifiedEpoch': 7, - 'lastFinalizedEpoch': 3, + assert_equal(fork2.getblockcount(), 30) + assert_finalizationstate(fork2, {'currentDynasty': 2, + 'currentEpoch': 6, + 'lastJustifiedEpoch': 5, + 'lastFinalizedEpoch': 2, 'validators': 1}) sync_node_to_fork(node, fork2) - assert_finalizationstate(node, {'currentDynasty': 3, - 'currentEpoch': 8, - 'lastJustifiedEpoch': 7, - 'lastFinalizedEpoch': 3, + assert_finalizationstate(node, {'currentDynasty': 2, + 'currentEpoch': 6, + 'lastJustifiedEpoch': 5, + 'lastFinalizedEpoch': 2, 'validators': 1}) self.log.info('node successfully switched to the longest justified fork') # create longer justified epoch on the previous fork1 # node must switch ("zag") to this fork - # J J - # - e6 - e7 - e8 - e9 - e10 fork1 node - # F F F F J | - # e0 - e1 - e2 - e3 - e4 - e5 - - # | J - # - e6 - e7 - e8 fork2 + # J J + # - e4 - e5 - e6 - e7 - e8 fork1 node + # F F F | + # e0 - e1 - e2 - e3 - + # | J + # - e4 - e5 - e6 fork2 generate_block(fork1, count=5) sync_node_to_fork(finalizer, fork1) vtx1 = generate_epoch_and_vote(fork1, finalizer, finalizer_address, vtx1) - assert_equal(fork1.getblockcount(), 50) - assert_finalizationstate(fork1, {'currentDynasty': 3, - 'currentEpoch': 10, - 'lastJustifiedEpoch': 9, - 'lastFinalizedEpoch': 3, + assert_equal(fork1.getblockcount(), 40) + assert_finalizationstate(fork1, {'currentDynasty': 2, + 'currentEpoch': 8, + 'lastJustifiedEpoch': 7, + 'lastFinalizedEpoch': 2, 'validators': 1}) assert_not_equal(fork1.getbestblockhash(), fork2.getbestblockhash()) sync_node_to_fork(node, fork1) - assert_finalizationstate(node, {'currentDynasty': 3, - 'currentEpoch': 10, - 'lastJustifiedEpoch': 9, - 'lastFinalizedEpoch': 3, + assert_finalizationstate(node, {'currentDynasty': 2, + 'currentEpoch': 8, + 'lastJustifiedEpoch': 7, + 'lastFinalizedEpoch': 2, 'validators': 1}) self.log.info('node successfully switched back to the longest justified fork') # UNIT-E TODO: node must follow longest finalized chain # node follows longest finalization - # J J - # - e6 - e7 - e8 - e9 - e10 fork1 node - # F F F F J | - # e0 - e1 - e2 - e3 - e4 - e5 - - # | F J - # - e6 - e7 - e8 - e9 fork2 - + # J J + # - e4 - e5 - e6 - e7 - e8 fork1 node + # F F F | + # e0 - e1 - e2 - e3 - + # | J F + # - e4 - e5 - e6 - e7 fork2 sync_node_to_fork(finalizer, fork2, force=True) vtx2 = generate_epoch_and_vote(fork2, finalizer, finalizer_address, vtx2) - assert_equal(fork2.getblockcount(), 45) - assert_finalizationstate(fork2, {'currentDynasty': 3, - 'currentEpoch': 9, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 7, + assert_equal(fork2.getblockcount(), 35) + assert_finalizationstate(fork2, {'currentDynasty': 2, + 'currentEpoch': 7, + 'lastJustifiedEpoch': 6, + 'lastFinalizedEpoch': 6, 'validators': 1}) # UNIT-E TODO: fix here diff --git a/test/functional/feature_reindex_commits.py b/test/functional/feature_reindex_commits.py index caea13c94f..75507b8e21 100755 --- a/test/functional/feature_reindex_commits.py +++ b/test/functional/feature_reindex_commits.py @@ -21,6 +21,7 @@ MIN_DEPOSIT = 1500 + class FeatureReindexCommits(UnitETestFramework): def get_extra_args(self, reindex): @@ -72,7 +73,7 @@ def run_test(self): assert_finalizationstate(self.proposer, {'currentEpoch': 7, 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + 'lastFinalizedEpoch': 6, 'validators': 1}) self.log.info("Restart nodes, -reindex=1") @@ -87,10 +88,10 @@ def run_test(self): assert_equal(len(votes), 2) assert_equal(self.proposer.getblockcount(), 45) assert_finalizationstate(self.proposer, - {'currentEpoch': 9, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 7, - 'validators': 1}) + {'currentEpoch': 9, + 'lastJustifiedEpoch': 8, + 'lastFinalizedEpoch': 8, + 'validators': 1}) self.log.info("Restart nodes, -reindex=0") self.restart_nodes(reindex=False) @@ -106,7 +107,7 @@ def run_test(self): assert_finalizationstate(self.proposer, {'currentEpoch': 11, 'lastJustifiedEpoch': 10, - 'lastFinalizedEpoch': 9, + 'lastFinalizedEpoch': 10, 'validators': 1}) def setup_deposit(self): diff --git a/test/functional/feature_snapshot.py b/test/functional/feature_snapshot.py index 4350e4866f..e4464a4edb 100755 --- a/test/functional/feature_snapshot.py +++ b/test/functional/feature_snapshot.py @@ -125,7 +125,7 @@ def has_snapshot(node, height): generate_block(full_node) assert_equal(full_node.getblockcount(), 11) wait_until(lambda: has_finalized_snapshot(full_node, height=4), timeout=5) - wait_until(lambda: has_snapshot(full_node, height=9), timeout=5) + wait_until(lambda: has_finalized_snapshot(full_node, height=9), timeout=5) assert_equal(len(full_node.listsnapshots()), 2) connect_nodes(blank_node, full_node.index) sync_blocks([blank_node, full_node]) @@ -139,16 +139,15 @@ def has_snapshot(node, height): connect_nodes(isd_node, blank_node.index) connect_nodes(isd_node, full_node.index) sync_blocks([full_node, isd_node]) - wait_until(lambda: has_finalized_snapshot(isd_node, height=4), timeout=5) wait_until(lambda: has_snapshot(isd_node, height=9), timeout=5) - assert_equal(full_node.listsnapshots(), isd_node.listsnapshots()) + assert_equal(len(isd_node.listsnapshots()), 1) chain = isd_node.getblockchaininfo() assert_equal(chain['headers'], 11) assert_equal(chain['blocks'], 11) assert_equal(chain['initialblockdownload'], False) assert_equal(chain['initialsnapshotdownload'], False) assert_equal(chain['pruned'], True) - assert_equal(chain['pruneheight'], 5) + assert_equal(chain['pruneheight'], 10) assert_equal(full_node.gettxoutsetinfo(), isd_node.gettxoutsetinfo()) # test that isd_node can be restarted @@ -182,9 +181,9 @@ def has_snapshot(node, height): # test that reorg one epoch after finalization is possible # s0 s1 - # G------------(h=3)...(h=8)-(h=9)-(h=10) full_node, blank_node - # \ - # -------------------(h=15) rework_node, isd_node + # G------------(h=3)...(h=8)-(h=9)-(h=10)-(h=11) full_node, blank_node + # \ + # ---------------(h=15) rework_node, isd_node connect_nodes(isd_node, rework_node.index) sync_blocks([isd_node, rework_node]) assert_equal(isd_node.getblockcount(), 15) diff --git a/test/functional/feature_snapshot_creation.py b/test/functional/feature_snapshot_creation.py index d5f7105474..9240b4b504 100755 --- a/test/functional/feature_snapshot_creation.py +++ b/test/functional/feature_snapshot_creation.py @@ -80,7 +80,7 @@ def has_valid_snapshot_for_height(node, height): assert_equal(node.getblockcount(), 41) assert_finalizationstate(node, {'currentDynasty': 6, 'currentEpoch': 9, - 'lastJustifiedEpoch': 7, + 'lastJustifiedEpoch': 6, 'lastFinalizedEpoch': 6, 'validators': 1}) diff --git a/test/functional/feature_snapshot_finalization.py b/test/functional/feature_snapshot_finalization.py index 884b9fb065..2bc87f44c6 100755 --- a/test/functional/feature_snapshot_finalization.py +++ b/test/functional/feature_snapshot_finalization.py @@ -74,7 +74,7 @@ def run_test(self): assert_equal(p.getblockcount(), 32) assert_finalizationstate(p, {'currentEpoch': 7, 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + 'lastFinalizedEpoch': 6, 'validators': 1}) self.log.info("Connect fast-sync node") @@ -83,7 +83,7 @@ def run_test(self): assert_finalizationstate(s, {'currentEpoch': 7, 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + 'lastFinalizedEpoch': 6, 'validators': 1}) self.log.info("Generate next epoch") @@ -92,13 +92,13 @@ def run_test(self): assert_equal(p.getblockcount(), 37) assert_finalizationstate(p, {'currentEpoch': 8, 'lastJustifiedEpoch': 7, - 'lastFinalizedEpoch': 6, + 'lastFinalizedEpoch': 7, 'validators': 1}) sync_blocks([p, s]) assert_finalizationstate(s, {'currentEpoch': 8, 'lastJustifiedEpoch': 7, - 'lastFinalizedEpoch': 6, + 'lastFinalizedEpoch': 7, 'validators': 1}) self.log.info("Check slashing condition") diff --git a/test/functional/finalization_deposit.py b/test/functional/finalization_deposit.py index 2636317824..e958ded583 100755 --- a/test/functional/finalization_deposit.py +++ b/test/functional/finalization_deposit.py @@ -109,18 +109,18 @@ def test_successful_deposit(self, finalizer, proposer): 'lastFinalizedEpoch': 0, 'validators': 0}) - # the finalizer will be ready to operate at currentDynasty=3 - for _ in range(4): + # the finalizer will be ready to operate at currentDynasty=2 + for _ in range(2): proposer.generate(10) assert_finalizationstate(proposer, {'validators': 0}) # start new dynasty proposer.generate(1) - assert_equal(proposer.getblockcount(), 51) - assert_finalizationstate(proposer, {'currentEpoch': 6, - 'currentDynasty': 3, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + assert_equal(proposer.getblockcount(), 31) + assert_finalizationstate(proposer, {'currentEpoch': 4, + 'currentDynasty': 2, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 1}) connect_nodes(finalizer, proposer.index) diff --git a/test/functional/finalization_deposit_reorg.py b/test/functional/finalization_deposit_reorg.py index 4f1119b623..425ef9cacb 100755 --- a/test/functional/finalization_deposit_reorg.py +++ b/test/functional/finalization_deposit_reorg.py @@ -194,7 +194,7 @@ def run_test(self): # finalize deposit and re-org to that fork # t1 t2 d1 - # e0 - e1[1, 2, 3, 4, ... ] - ... - e5[51] node0, finalizer + # e0 - e1[1, 2, 3, 4, ... ] - ... - e4[31] node0, finalizer # | | # | -- 4, 5] node2 # | t1 t2 d1 @@ -202,17 +202,17 @@ def run_test(self): generate_block(node0, count=6) assert_equal(node0.getblockcount(), 10) assert_equal(node0.getfinalizationstate()['currentDynasty'], 0) - for _ in range(4): + for _ in range(2): generate_block(node0, count=10) - assert_equal(node0.getblockcount(), 50) - assert_equal(node0.getfinalizationstate()['currentDynasty'], 2) + assert_equal(node0.getblockcount(), 30) + assert_equal(node0.getfinalizationstate()['currentDynasty'], 1) connect_nodes(node0, finalizer.index) sync_blocks([node0, finalizer], timeout=60) assert_equal(finalizer.getvalidatorinfo()['validator_status'], 'WAITING_DEPOSIT_FINALIZATION') generate_block(node0) - assert_equal(node0.getfinalizationstate()['currentDynasty'], 3) + assert_equal(node0.getfinalizationstate()['currentDynasty'], 2) sync_blocks([node0, finalizer], timeout=10) assert_equal(finalizer.getvalidatorinfo()['validator_status'], 'IS_VALIDATING') self.log.info('validator_status is correct after re-organizing to the fork of finalized deposit') diff --git a/test/functional/finalization_expired_vote_conflict.py b/test/functional/finalization_expired_vote_conflict.py index bd3c96cc86..24a19ac884 100755 --- a/test/functional/finalization_expired_vote_conflict.py +++ b/test/functional/finalization_expired_vote_conflict.py @@ -79,20 +79,20 @@ def run_test(self): self.generate_epochs_without_mempool_sync(6) # First few epochs should be insta-finalized, but after that # finalization should not happen since validator is detached - assert_equal(3, self.last_finalized_epoch) + assert_equal(2, self.last_finalized_epoch) # Validator is back, finalization should now work relay.relay_txs = True self.generate_epochs(4) - assert_equal(9, self.last_finalized_epoch) + assert_equal(10, self.last_finalized_epoch) # Checking behaviour when votes are casted on top of other votes relay.relay_txs = False self.generate_epochs_without_mempool_sync(4) - assert_equal(9, self.last_finalized_epoch) + assert_equal(10, self.last_finalized_epoch) relay.relay_txs = True self.generate_epochs(4) - assert_equal(17, self.last_finalized_epoch) + assert_equal(18, self.last_finalized_epoch) # Checking behaviour when votes are casted on top of logout logout = validator.logout() @@ -100,7 +100,7 @@ def run_test(self): relay.relay_txs = False self.generate_epochs_without_mempool_sync(10) - assert_equal(17, self.last_finalized_epoch) + assert_equal(18, self.last_finalized_epoch) assert_log_does_not_contain(validator, "error") diff --git a/test/functional/finalization_logout.py b/test/functional/finalization_logout.py index c7533848cf..47e1088dfc 100755 --- a/test/functional/finalization_logout.py +++ b/test/functional/finalization_logout.py @@ -78,12 +78,12 @@ def run_test(self): disconnect_nodes(finalizer2, proposer.index) # Generate enough blocks to advance 3 dynasties and have active finalizers - proposer.generate(5 * 10) - assert_equal(proposer.getblockcount(), 51) - assert_finalizationstate(proposer, {'currentEpoch': 6, - 'currentDynasty': 3, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + proposer.generate(3 * 10) + assert_equal(proposer.getblockcount(), 31) + assert_finalizationstate(proposer, {'currentEpoch': 4, + 'currentDynasty': 2, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 2}) self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=proposer) @@ -91,11 +91,11 @@ def run_test(self): # Mine the votes to avoid the next logout to conflict with a vote in the mempool proposer.generate(1) - assert_equal(proposer.getblockcount(), 52) + assert_equal(proposer.getblockcount(), 32) - # Logout included in dynasty=3 - # At dynasty=3+3=6 finalizer is still voting - # At dynasty=7 finalizer doesn't vote + # Logout included in dynasty=2 + # At dynasty=2+3=5 finalizer is still voting + # At dynasty=6 doesn't vote connect_nodes(finalizer1, proposer.index) sync_blocks([finalizer1, proposer], timeout=10) logout_tx = finalizer1.logout() @@ -112,34 +112,34 @@ def run_test(self): # Mine votes and move to checkpoint proposer.generate(9) - assert_equal(proposer.getblockcount(), 70) - assert_finalizationstate(proposer, {'currentEpoch': 7, - 'currentDynasty': 4, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + assert_equal(proposer.getblockcount(), 50) + assert_finalizationstate(proposer, {'currentEpoch': 5, + 'currentDynasty': 3, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4, 'validators': 2}) - # Check that the finalizer is still voting up to dynasty=6 (including) + # Check that the finalizer is still voting up to dynasty=5 (including) for _ in range(2): proposer.generate(1) self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=proposer) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) proposer.generate(9) - assert_equal(proposer.getblockcount(), 90) - assert_finalizationstate(proposer, {'currentEpoch': 9, - 'currentDynasty': 6, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 7, + assert_equal(proposer.getblockcount(), 70) + assert_finalizationstate(proposer, {'currentEpoch': 7, + 'currentDynasty': 5, + 'lastJustifiedEpoch': 6, + 'lastFinalizedEpoch': 6, 'validators': 2}) # finalizer1 is logged out proposer.generate(1) - assert_equal(proposer.getblockcount(), 91) - assert_finalizationstate(proposer, {'currentEpoch': 10, - 'currentDynasty': 7, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 7, + assert_equal(proposer.getblockcount(), 71) + assert_finalizationstate(proposer, {'currentEpoch': 8, + 'currentDynasty': 6, + 'lastJustifiedEpoch': 6, + 'lastFinalizedEpoch': 6, 'validators': 1}) # finalizer1 is not validating so we can keep it connected @@ -152,11 +152,11 @@ def run_test(self): self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) proposer.generate(9) sync_blocks([proposer, finalizer1], timeout=20) - assert_equal(proposer.getblockcount(), 100) - assert_finalizationstate(proposer, {'currentEpoch': 10, - 'currentDynasty': 7, - 'lastJustifiedEpoch': 9, - 'lastFinalizedEpoch': 8, + assert_equal(proposer.getblockcount(), 80) + assert_finalizationstate(proposer, {'currentEpoch': 8, + 'currentDynasty': 6, + 'lastJustifiedEpoch': 7, + 'lastFinalizedEpoch': 7, 'validators': 1}) # check that we cannot deposit again before we withdraw @@ -165,5 +165,6 @@ def run_test(self): finalizer1.getnewaddress("", "legacy"), 1500) + if __name__ == '__main__': LogoutTest().main() diff --git a/test/functional/finalization_slash.py b/test/functional/finalization_slash.py index bb60aab80f..e83a19ef55 100755 --- a/test/functional/finalization_slash.py +++ b/test/functional/finalization_slash.py @@ -92,13 +92,13 @@ def corrupt_script(script, n_byte): disconnect_nodes(fork1, finalizer2.index) # pass instant finalization - # F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e[26] fork1, fork2 - generate_block(fork1, count=3 + 5 + 5 + 5 + 5 + 1) - assert_equal(fork1.getblockcount(), 26) - assert_finalizationstate(fork1, {'currentEpoch': 6, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + # F F F + # e0 - e1 - e2 - e3 - e4[16] fork1, fork2 + generate_block(fork1, count=3 + 5 + 5 + 1) + assert_equal(fork1.getblockcount(), 16) + assert_finalizationstate(fork1, {'currentEpoch': 4, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 1}) # change topology where forks are not connected @@ -110,19 +110,19 @@ def corrupt_script(script, n_byte): # test that same vote included on different forks # doesn't create a slash transaction - # v1 - # - e6[27, 28, 29, 30] fork1 - # F F F F F J / - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] - # \ v1 - # - e6[27, 28, 29, 30] fork2 + # v1 + # - e4[17, 18, 19, 20] fork1 + # F F F F / + # e0 - e1 - e2 - e3 - e4[16] + # \ v1 + # - e4[17, 18, 19, 20] fork2 self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=fork1) v1 = fork1.getrawtransaction(fork1.getrawmempool()[0]) generate_block(fork1, count=4) - assert_equal(fork1.getblockcount(), 30) - assert_finalizationstate(fork1, {'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4, + assert_equal(fork1.getblockcount(), 20) + assert_finalizationstate(fork1, {'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3, 'validators': 1}) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=fork2) @@ -130,35 +130,35 @@ def corrupt_script(script, n_byte): assert_raises_rpc_error(-27, 'transaction already in block chain', fork2.sendrawtransaction, v1) assert_equal(len(fork2.getrawmempool()), 0) generate_block(fork2, count=3) - assert_equal(fork2.getblockcount(), 30) - assert_finalizationstate(fork1, {'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4, + assert_equal(fork2.getblockcount(), 20) + assert_finalizationstate(fork1, {'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3, 'validators': 1}) self.log.info('same vote on two forks was accepted') # test that double-vote with invalid vote signature is ignored # and doesn't cause slashing - # v1 v2a - # - e6 - e7[31, 32] fork1 - # F F F F F F J / - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] - # \ v1 v2a - # - e6 - e7[31, 32] fork2 + # v1 v2a + # - e4 - e5[21, 22] fork1 + # F F F F F / + # e0 - e1 - e2 - e3 - e4[16] + # \ v1 v2a + # - e4 - e5[21, 22] fork2 generate_block(fork1) self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=fork1) v2a = fork1.getrawtransaction(fork1.getrawmempool()[0]) generate_block(fork1) - assert_equal(fork1.getblockcount(), 32) - assert_finalizationstate(fork1, {'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + assert_equal(fork1.getblockcount(), 22) + assert_finalizationstate(fork1, {'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4, 'validators': 1}) generate_block(fork2) tx_v2a = FromHex(CTransaction(), v2a) - # corrupt the 1st byte of the validators pubkey in the commit script + # corrupt the 1st byte of the validator's pubkey in the commit script # see schema in CScript::CommitScript tx_v2a.vout[0].scriptPubKey = corrupt_script(script=tx_v2a.vout[0].scriptPubKey, n_byte=2) @@ -173,21 +173,21 @@ def corrupt_script(script, n_byte): generate_block(fork2) assert_equal(len(fork2.getrawmempool()), 0) - assert_equal(fork2.getblockcount(), 32) - assert_finalizationstate(fork1, {'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + assert_equal(fork2.getblockcount(), 22) + assert_finalizationstate(fork1, {'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4, 'validators': 1}) self.log.info('double-vote with invalid signature is ignored') # test that valid double-vote but corrupt withdraw address # creates slash tx it is included in the next block - # v1 v2a - # - e6 - e7[31, 32] fork1 - # F F F F F F J / - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] - # \ v1 v2a s1 - # - e6 - e7[31, 32, 33] fork2 + # v1 v2a + # - e4 - e5[21, 22] fork1 + # F F F F F / + # e0 - e1 - e2 - e3 - e4[16] + # \ v1 v2a s1 + # - e4 - e5[21, 22, 23] fork2 # corrupt the 1st byte of the address in the scriptpubkey # but keep the correct vote signature see schema in CScript::CommitScript tx_v2a = FromHex(CTransaction(), v2a) @@ -202,8 +202,8 @@ def corrupt_script(script, n_byte): s1 = FromHex(CTransaction(), fork2.getrawtransaction(s1_hash)) assert_equal(s1.get_type(), TxType.SLASH) - b33 = generate_block(fork2)[0] - block = FromHex(CBlock(), fork2.getblock(b33, 0)) + b23 = generate_block(fork2)[0] + block = FromHex(CBlock(), fork2.getblock(b23, 0)) assert_equal(len(block.vtx), 2) block.vtx[1].rehash() assert_equal(block.vtx[1].hash, s1_hash) diff --git a/test/functional/finalization_slash_itself.py b/test/functional/finalization_slash_itself.py index d194ac3e9c..95d47e92cc 100755 --- a/test/functional/finalization_slash_itself.py +++ b/test/functional/finalization_slash_itself.py @@ -52,7 +52,6 @@ def setup_network(self): connect_nodes(fork1, finalizer2.index) def test_double_votes(self): - fork1 = self.nodes[0] fork2 = self.nodes[1] finalizer1 = self.nodes[2] @@ -82,13 +81,13 @@ def test_double_votes(self): disconnect_nodes(fork1, finalizer2.index) # pass instant finalization - # F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e[26] fork1, fork2 - generate_block(fork1, count=3 + 5 + 5 + 5 + 5 + 1) - assert_equal(fork1.getblockcount(), 26) - assert_finalizationstate(fork1, {'currentEpoch': 6, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + # F F F + # e0 - e1 - e2 - e3 - e4[16] fork1, fork2 + generate_block(fork1, count=3 + 5 + 5 + 1) + assert_equal(fork1.getblockcount(), 16) + assert_finalizationstate(fork1, {'currentEpoch': 4, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 1}) # change topology where forks are not connected @@ -100,31 +99,31 @@ def test_double_votes(self): # Create some blocks and cause finalizer to vote, then take the vote and send it to # finalizer2, when finalizer2 will vote it should not slash itself - # v1 v2a - # - e6 - e7[31, 32, 33] - e8[36] fork1 - # F F F F F F J / - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] - # \ v1 v2a - # - e6 - e7[31, 32] fork2 + # v1 v2a + # - e5 - e6[26, 27, 28] - e7[31] fork1 + # F F F F F / + # e0 - e1 - e2 - e3 - e4[16] + # \ v1 v2a + # - e5 - e6[26, 27] fork2 generate_block(fork1) self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=fork1) generate_block(fork1, count=5) raw_vote_1 = self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=fork1) generate_block(fork1) - assert_equal(fork1.getblockcount(), 33) - assert_finalizationstate(fork1, {'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + assert_equal(fork1.getblockcount(), 23) + assert_finalizationstate(fork1, {'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4, 'validators': 1}) # We'll use a second vote to check if there is slashing when a validator tries to send a double vote after it # voted. generate_block(fork1, count=3) raw_vote_2 = self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=fork1) - assert_equal(fork1.getblockcount(), 36) - assert_finalizationstate(fork1, {'currentEpoch': 8, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + assert_equal(fork1.getblockcount(), 26) + assert_finalizationstate(fork1, {'currentEpoch': 6, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4, 'validators': 1}) # Send the conflicting vote from the other chain to finalizer2, it should record it and slash it later @@ -133,10 +132,10 @@ def test_double_votes(self): generate_block(fork2) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=fork2) generate_block(fork2, count=5) - assert_equal(fork2.getblockcount(), 32) - assert_finalizationstate(fork2, {'currentEpoch': 7, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4, + assert_equal(fork2.getblockcount(), 22) + assert_finalizationstate(fork2, {'currentEpoch': 5, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3, 'validators': 1}) connect_nodes(finalizer2, fork2.index) @@ -154,10 +153,10 @@ def test_double_votes(self): # check if there is slashing after voting fork2.generatetoaddress(3, fork1.getnewaddress('', 'bech32')) - assert_equal(fork2.getblockcount(), 36) - assert_finalizationstate(fork2, {'currentEpoch': 8, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + assert_equal(fork2.getblockcount(), 26) + assert_finalizationstate(fork2, {'currentEpoch': 6, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4, 'validators': 1}) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=fork2) @@ -168,7 +167,6 @@ def test_double_votes(self): vote = finalizer2.decoderawtransaction(finalizer2.getrawtransaction(finalizer2.getrawmempool()[0])) assert_equal(vote['txtype'], TxType.VOTE.value) - def run_test(self): self.test_double_votes() diff --git a/test/functional/finalization_slash_restart.py b/test/functional/finalization_slash_restart.py index 30ca04f33e..d24a788795 100755 --- a/test/functional/finalization_slash_restart.py +++ b/test/functional/finalization_slash_restart.py @@ -62,8 +62,8 @@ def setup_deposit(self, proposer, finalizers): deptx = f.deposit(f.new_address, 1500) self.wait_for_transaction(deptx, nodes=[proposer]) - generate_block(proposer, count=21) - assert_equal(proposer.getblockcount(), 22) + generate_block(proposer, count=14) + assert_equal(proposer.getblockcount(), 15) def make_double_vote_tx(self, vote_tx, input_tx, proposer, finalizer): # To detect double vote, it's enough having two votes which are: @@ -97,7 +97,7 @@ def run_test(self): votes = self.generate_epoch(proposer=p, finalizer=f1, count=2) assert len(votes) != 0 - self.log.info("Check slashig condition after node restart") + self.log.info("Check slashing condition after node restart") self.restart_node(p) vtx = self.make_double_vote_tx(votes[0], votes[-1], p, f1) assert_raises_rpc_error(-26, 'bad-vote-invalid', p.sendrawtransaction, ToHex(vtx)) @@ -110,7 +110,7 @@ def run_test(self): votes = self.generate_epoch(proposer=p, finalizer=f2, count=2) assert len(votes) != 0 - self.log.info("Check slashig condition after node restart with reindex") + self.log.info("Check slashing condition after node restart with reindex") self.restart_node(p, reindex=True) vtx = self.make_double_vote_tx(votes[0], votes[-1], p, f2) assert_raises_rpc_error(-26, 'bad-vote-invalid', p.sendrawtransaction, ToHex(vtx)) diff --git a/test/functional/finalization_state_restoration.py b/test/functional/finalization_state_restoration.py index e0f175d8ca..ae3273839f 100755 --- a/test/functional/finalization_state_restoration.py +++ b/test/functional/finalization_state_restoration.py @@ -69,7 +69,7 @@ def run_test(self): assert_finalizationstate(p, {'currentEpoch': 7, 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + 'lastFinalizedEpoch': 6, 'validators': 1}) self.log.info("Restarting proposer") @@ -85,7 +85,7 @@ def run_test(self): # it is not connected to validator so that finalization shouldn't move assert_finalizationstate(p, {'currentEpoch': 9, 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + 'lastFinalizedEpoch': 6, 'validators': 1}) # connect validator and check how it votes @@ -95,7 +95,7 @@ def run_test(self): assert_equal(p.getblockcount(), 45) assert_finalizationstate(p, {'currentEpoch': 9, 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 5, + 'lastFinalizedEpoch': 6, 'validators': 1}) self.generate_epoch(p, v, count=2) @@ -103,7 +103,7 @@ def run_test(self): assert_equal(p.getblockcount(), 55) assert_finalizationstate(p, {'currentEpoch': 11, 'lastJustifiedEpoch': 10, - 'lastFinalizedEpoch': 9, + 'lastFinalizedEpoch': 10, 'validators': 1}) self.log.info("Restarting validator") @@ -117,7 +117,7 @@ def run_test(self): assert_equal(p.getblockcount(), 65) assert_finalizationstate(p, {'currentEpoch': 13, 'lastJustifiedEpoch': 12, - 'lastFinalizedEpoch': 11, + 'lastFinalizedEpoch': 12, 'validators': 1}) connect_nodes(p, v.index) sync_blocks([p, v]) @@ -133,14 +133,14 @@ def run_test(self): sync_blocks([p, v]) assert_finalizationstate(p, {'currentEpoch': 13, 'lastJustifiedEpoch': 12, - 'lastFinalizedEpoch': 11, + 'lastFinalizedEpoch': 12, 'validators': 1}) self.log.info("Restart proposer") self.restart_node(p) assert_finalizationstate(p, {'currentEpoch': 13, 'lastJustifiedEpoch': 12, - 'lastFinalizedEpoch': 11, + 'lastFinalizedEpoch': 12, 'validators': 1}) if __name__ == '__main__': diff --git a/test/functional/finalization_vote.py b/test/functional/finalization_vote.py index 606c3a0a46..4c01c66164 100755 --- a/test/functional/finalization_vote.py +++ b/test/functional/finalization_vote.py @@ -75,13 +75,13 @@ def run_test(self): assert_equal(len(node0.getpeerinfo()), 0) # move tip to the height when finalizers are activated - # complete epoch + 3 epochs + 1 block of new epoch - generate_block(node0, count=4 + 5 + 5 + 5 + 5 + 1) - assert_equal(node0.getblockcount(), 26) - assert_finalizationstate(node0, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + # complete epoch + 2 epochs + 1 block of new epoch + generate_block(node0, count=4 + 5 + 5 + 1) + assert_equal(node0.getblockcount(), 16) + assert_finalizationstate(node0, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 3}) # test that finalizers vote after processing 1st block of new epoch @@ -91,11 +91,11 @@ def run_test(self): assert_equal(len(node0.getrawmempool()), 3) generate_block(node0, count=4) - assert_equal(node0.getblockcount(), 30) - assert_finalizationstate(node0, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4, + assert_equal(node0.getblockcount(), 20) + assert_finalizationstate(node0, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3, 'validators': 3}) self.log.info('Finalizers voted after first block of new epoch') @@ -105,7 +105,7 @@ def run_test(self): self.restart_node(finalizer3.index, ['-validating=1', '-finalizervotefromepochblocknumber=3']) generate_block(node0) - assert_equal(node0.getblockcount(), 31) + assert_equal(node0.getblockcount(), 21) self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node0) connect_nodes(finalizer2, node0.index) connect_nodes(finalizer3, node0.index) @@ -115,7 +115,7 @@ def run_test(self): disconnect_nodes(finalizer3, node0.index) generate_block(node0) - assert_equal(node0.getblockcount(), 32) + assert_equal(node0.getblockcount(), 22) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=node0) connect_nodes(finalizer3, node0.index) sync_blocks([finalizer3, node0], timeout=10) @@ -123,29 +123,29 @@ def run_test(self): disconnect_nodes(finalizer3, node0.index) generate_block(node0) - assert_equal(node0.getblockcount(), 33) + assert_equal(node0.getblockcount(), 23) self.wait_for_vote_and_disconnect(finalizer=finalizer3, node=node0) generate_block(node0, count=2) - assert_equal(node0.getblockcount(), 35) - assert_finalizationstate(node0, {'currentDynasty': 4, - 'currentEpoch': 7, - 'lastJustifiedEpoch': 6, - 'lastFinalizedEpoch': 5, + assert_equal(node0.getblockcount(), 25) + assert_finalizationstate(node0, {'currentDynasty': 3, + 'currentEpoch': 5, + 'lastJustifiedEpoch': 4, + 'lastFinalizedEpoch': 4, 'validators': 3}) self.log.info('Finalizers voted on a configured block number') # test that finalizers can vote after configured epoch block number generate_block(node0, count=4) - assert_equal(node0.getblockcount(), 39) + assert_equal(node0.getblockcount(), 29) prev_tx = self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node0) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=node0) self.wait_for_vote_and_disconnect(finalizer=finalizer3, node=node0) generate_block(node0) - assert_equal(node0.getblockcount(), 40) - assert_finalizationstate(node0, {'currentDynasty': 5, - 'currentEpoch': 8, - 'lastJustifiedEpoch': 7, - 'lastFinalizedEpoch': 6, + assert_equal(node0.getblockcount(), 30) + assert_finalizationstate(node0, {'currentDynasty': 4, + 'currentEpoch': 6, + 'lastJustifiedEpoch': 5, + 'lastFinalizedEpoch': 5, 'validators': 3}) self.log.info('Finalizers voted after configured block number') diff --git a/test/functional/finalization_vote_reorg.py b/test/functional/finalization_vote_reorg.py index 854e528ef8..d414582cde 100755 --- a/test/functional/finalization_vote_reorg.py +++ b/test/functional/finalization_vote_reorg.py @@ -78,140 +78,140 @@ def assert_vote(vote_raw_tx, input_raw_tx, source_epoch, target_epoch, target_ha disconnect_nodes(fork0, finalizer2.index) # leave instant justification - # F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] - generate_block(fork0, count=3 + 5 + 5 + 5 + 5 + 1) - assert_equal(fork0.getblockcount(), 26) - assert_finalizationstate(fork0, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + # F F F + # e0 - e1 - e2 - e3 - e4[16] + generate_block(fork0, count=3 + 5 + 5 + 1) + assert_equal(fork0.getblockcount(), 16) + assert_finalizationstate(fork0, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 2}) # move tip to one block before checkpoint to be able to # revert checkpoint on the fork - # J v0 - # ... - e5 - e6[26, 27, 28, 29] fork0 + # F v0 + # ... - e3 - e4[16, 17, 18, 19] fork0, finalizer # \ # - fork1 v0 = self.wait_for_vote_and_disconnect(finalizer=finalizer, node=fork0) - assert_vote(vote_raw_tx=v0, input_raw_tx=d1, source_epoch=4, target_epoch=5, target_hash=fork0.getblockhash(25)) + assert_vote(vote_raw_tx=v0, input_raw_tx=d1, source_epoch=2, target_epoch=3, target_hash=fork0.getblockhash(15)) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=fork0) generate_block(fork0, count=3) sync_blocks([fork0, fork1], timeout=10) disconnect_nodes(fork0, fork1.index) - assert_equal(fork0.getblockcount(), 29) - assert_finalizationstate(fork0, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4, + assert_equal(fork0.getblockcount(), 19) + assert_finalizationstate(fork0, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3, 'validators': 2}) - # vote v1 on target_epoch=6 target_hash=30 - # J v0 v1 - # ... - e5 - e6[26, 27, 28, 29, 30] - e7[31, 32] fork0 + # vote v1 on target_epoch=4 target_hash=20 + # F v0 v1 + # ... - e3 - e4[16, 17, 18, 19, 20] - e5[21, 22] fork0, finalizer # \ # - fork1 generate_block(fork0, count=2) - assert_equal(fork0.getblockcount(), 31) + assert_equal(fork0.getblockcount(), 21) v1 = self.wait_for_vote_and_disconnect(finalizer=finalizer, node=fork0) - assert_vote(vote_raw_tx=v1, input_raw_tx=v0, source_epoch=5, target_epoch=6, target_hash=fork0.getblockhash(30)) + assert_vote(vote_raw_tx=v1, input_raw_tx=v0, source_epoch=3, target_epoch=4, target_hash=fork0.getblockhash(20)) generate_block(fork0) connect_nodes(finalizer, fork0.index) sync_blocks([finalizer, fork0], timeout=10) disconnect_nodes(finalizer, fork0.index) - assert_equal(fork0.getblockcount(), 32) - assert_equal(finalizer.getblockcount(), 32) + assert_equal(fork0.getblockcount(), 22) + assert_equal(finalizer.getblockcount(), 22) self.log.info('finalizer successfully voted on the checkpoint') # re-org last checkpoint and check that finalizer doesn't vote - # J v0 v1 - # ... - e5 - e6[26, 27, 28, 29, 30] - e7[31, 32] fork0 + # F v0 v1 + # ... - e3 - e4[16, 17, 18, 19, 20] - e5[21, 22] fork0 # \ - # - 30] - e7[31, 32, 33] fork1 + # - 20] - e5[21, 22, 23] fork1, finalizer generate_block(fork1, count=4) - assert_equal(fork1.getblockcount(), 33) + assert_equal(fork1.getblockcount(), 23) connect_nodes(finalizer, fork1.index) sync_blocks([finalizer, fork1], timeout=10) - assert_equal(finalizer.getblockcount(), 33) + assert_equal(finalizer.getblockcount(), 23) assert_equal(len(fork1.getrawmempool()), 0) disconnect_nodes(finalizer, fork1.index) self.log.info('finalizer successfully detected potential double vote and did not vote') # continue to new epoch and check that finalizer votes on fork1 - # J v0 v1 - # ... - e5 - e6[26, 27, 28, 29, 30] - e7[31, 32] fork0 - # \ v2 - # - 30] - e7[...] - e8[36, 37] fork1 + # F v0 v1 + # ... - e3 - e4[16, 17, 18, 19, 20] - e5[21, 22] fork0 + # \ v2 + # - 20] - e5[ ... ] - e6[26, 27] fork1, finalizer generate_block(fork1, count=3) - assert_equal(fork1.getblockcount(), 36) + assert_equal(fork1.getblockcount(), 26) v2 = self.wait_for_vote_and_disconnect(finalizer=finalizer, node=fork1) - assert_vote(vote_raw_tx=v2, input_raw_tx=v0, source_epoch=5, target_epoch=7, target_hash=fork1.getblockhash(35)) + assert_vote(vote_raw_tx=v2, input_raw_tx=v0, source_epoch=3, target_epoch=5, target_hash=fork1.getblockhash(25)) generate_block(fork1) - assert_equal(fork1.getblockcount(), 37) + assert_equal(fork1.getblockcount(), 27) # create new epoch on fork1 and check that finalizer votes - # J v0 v1 - # ... - e5 - e6[26, 27, 28, 29, 30] - e7[31, 32] fork0 - # \ v2 v3 - # - 30] - e7[...] - e8[36, 37, ...] - e9[41, 42] fork1 + # F v0 v1 + # ... - e3 - e4[16, 17, 18, 19, 20] - e5[21, 22] fork0 + # \ v2 v3 + # - 20] - e5[ ... ] - e6[26, 27, ...] - e7[31, 32] fork1, finalizer generate_block(fork1, count=4) - assert_equal(fork1.getblockcount(), 41) + assert_equal(fork1.getblockcount(), 31) v3 = self.wait_for_vote_and_disconnect(finalizer=finalizer, node=fork1) - assert_vote(vote_raw_tx=v3, input_raw_tx=v2, source_epoch=5, target_epoch=8, target_hash=fork1.getblockhash(40)) + assert_vote(vote_raw_tx=v3, input_raw_tx=v2, source_epoch=3, target_epoch=6, target_hash=fork1.getblockhash(30)) generate_block(fork1) - assert_equal(fork1.getblockcount(), 42) + assert_equal(fork1.getblockcount(), 32) # create longer fork0 and check that after reorg finalizer doesn't vote - # J v0 v1 - # ... - e5 - e6[26, 27, 28, 29, 30] - e7 - e8 - e9[41,42, 43] fork0 + # F v0 v1 + # ... - e3 - e4[16, 17, 18, 19, 20] - e5 - e6 - e7[31, 32, 33] fork0, finalizer # \ v2 v3 - # - 30] - e7 - e8 - e9[41, 42] fork1 + # - 20] - e5 - e6 - e7[31, 32] fork1 generate_block(fork0, count=11) - assert_equal(fork0.getblockcount(), 43) + assert_equal(fork0.getblockcount(), 33) connect_nodes(finalizer, fork0.index) sync_blocks([finalizer, fork0]) - assert_equal(finalizer.getblockcount(), 43) + assert_equal(finalizer.getblockcount(), 33) assert_equal(len(fork0.getrawmempool()), 0) disconnect_nodes(finalizer, fork0.index) self.log.info('finalizer successfully detected potential two consecutive double votes and did not vote') # check that finalizer can vote from next epoch on fork0 - # J v0 v1 v4 - # ... - e5 - e6[26, 27, 28, 29, 30] - e7 - e8 - e9[...] - e10[46, 47] fork0 + # F v0 v1 v4 + # ... - e3 - e4[16, 17, 18, 19, 20] - e5 - e6 - e7[ ... ] - e8[36, 37] fork0, finalizer # \ v2 v3 - # - 30] - e7 - e8 - e9[41, 42] fork1 + # - 20] - e5 - e6 - e7[31, 32] fork1 generate_block(fork0, count=3) - assert_equal(fork0.getblockcount(), 46) + assert_equal(fork0.getblockcount(), 36) v4 = self.wait_for_vote_and_disconnect(finalizer=finalizer, node=fork0) - assert_vote(vote_raw_tx=v4, input_raw_tx=v1, source_epoch=5, target_epoch=9, target_hash=fork0.getblockhash(45)) + assert_vote(vote_raw_tx=v4, input_raw_tx=v1, source_epoch=3, target_epoch=7, target_hash=fork0.getblockhash(35)) generate_block(fork0) - assert_equal(fork0.getblockcount(), 47) + assert_equal(fork0.getblockcount(), 37) # finalize epoch8 on fork1 and re-broadcast all vote txs # which must not create slash tx - # J v0 v1 v4 - # ... - e5 - e6[26, 27, 28, 29, 30] - e7 - e8[ ... ] - e9[...] - e10[46, 47] fork0 - # \ F v2 J v3 - # - 30] - e7 - e8[36, 37,...] - e9[41, 42, 43] - e10[46, 47] fork1 + # F v0 v1 v4 + # ... - e3 - e4[16, 17, 18, 19, 20] - e5 - e6[ ... ] - e7[ ... ] - e8[36, 37] fork0, finalizer + # \ J v2 v3 + # - 20] - e5 - e6[26, 27, ...] - e7[31, 32, 33] - e8[36, 37] fork1 self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=fork1) generate_block(fork1) - assert_equal(fork1.getblockcount(), 43) - assert_finalizationstate(fork1, {'currentDynasty': 4, - 'currentEpoch': 9, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 4, + assert_equal(fork1.getblockcount(), 33) + assert_finalizationstate(fork1, {'currentDynasty': 3, + 'currentEpoch': 7, + 'lastJustifiedEpoch': 6, + 'lastFinalizedEpoch': 3, 'validators': 2}) generate_block(fork1, count=3) - assert_equal(fork1.getblockcount(), 46) + assert_equal(fork1.getblockcount(), 36) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=fork1) generate_block(fork1) - assert_equal(fork1.getblockcount(), 47) - assert_finalizationstate(fork1, {'currentDynasty': 4, - 'currentEpoch': 10, - 'lastJustifiedEpoch': 9, - 'lastFinalizedEpoch': 8, + assert_equal(fork1.getblockcount(), 37) + assert_finalizationstate(fork1, {'currentDynasty': 3, + 'currentEpoch': 8, + 'lastJustifiedEpoch': 7, + 'lastFinalizedEpoch': 7, 'validators': 2}) assert_raises_rpc_error(-26, 'bad-vote-invalid', fork1.sendrawtransaction, v1) diff --git a/test/functional/finalization_withdraw.py b/test/functional/finalization_withdraw.py index de2bafdd6d..65937e2dd1 100755 --- a/test/functional/finalization_withdraw.py +++ b/test/functional/finalization_withdraw.py @@ -59,6 +59,7 @@ def run_test(self): finalizer2 = self.nodes[2] self.setup_stake_coins(*self.nodes) + assert_equal(finalizer1.getbalance(), Decimal('10000')) # Leave IBD generate_block(proposer) @@ -88,31 +89,31 @@ def run_test(self): self.log.info('deposits are created') # Generate enough blocks to activate deposits - # F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] + # F F F F + # e0 - e1 - e2 - e3 - e4[16] # d1 # d2 - generate_block(proposer, count=3 + 5 + 5 + 5 + 5) - assert_equal(proposer.getblockcount(), 25) - assert_finalizationstate(proposer, {'currentDynasty': 2, - 'currentEpoch': 5, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + generate_block(proposer, count=3 + 5 + 5) + assert_equal(proposer.getblockcount(), 15) + assert_finalizationstate(proposer, {'currentDynasty': 1, + 'currentEpoch': 3, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 0}) generate_block(proposer) - assert_equal(proposer.getblockcount(), 26) - assert_finalizationstate(proposer, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 4, - 'lastFinalizedEpoch': 3, + assert_equal(proposer.getblockcount(), 16) + assert_finalizationstate(proposer, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 2, + 'lastFinalizedEpoch': 2, 'validators': 2}) self.log.info('finalizers are created') # Logout finalizer1 - # F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] - # d1 l1 + # F F F F + # e0 - e1 - e2 - e3 - e4[16, 17, 18, 19, 20] + # d1 l1 # d2 self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=proposer) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) @@ -128,13 +129,13 @@ def run_test(self): disconnect_nodes(finalizer1, proposer.index) generate_block(proposer, count=3) - assert_equal(proposer.getblockcount(), 30) - assert_finalizationstate(proposer, {'currentDynasty': 3, - 'currentEpoch': 6, - 'lastJustifiedEpoch': 5, - 'lastFinalizedEpoch': 4, + assert_equal(proposer.getblockcount(), 20) + assert_finalizationstate(proposer, {'currentDynasty': 2, + 'currentEpoch': 4, + 'lastJustifiedEpoch': 3, + 'lastFinalizedEpoch': 3, 'validators': 2}) - self.log.info('finalizer1 logged out in dynasty=3') + self.log.info('finalizer1 logged out in dynasty=2') # During LOGOUT_DYNASTY_DELAY both finalizers can vote. # Since the finalization happens at every epoch, @@ -149,28 +150,28 @@ def run_test(self): finalizer1.withdraw, finalizer1_address) - assert_equal(proposer.getblockcount(), 45) - assert_finalizationstate(proposer, {'currentDynasty': 6, - 'currentEpoch': 9, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 7, + assert_equal(proposer.getblockcount(), 35) + assert_finalizationstate(proposer, {'currentDynasty': 5, + 'currentEpoch': 7, + 'lastJustifiedEpoch': 6, + 'lastFinalizedEpoch': 6, 'validators': 2}) self.log.info('finalizer1 voted during logout delay successfully') # During WITHDRAW_DELAY finalizer1 can't vote and can't withdraw generate_block(proposer) - assert_finalizationstate(proposer, {'currentDynasty': 7, - 'currentEpoch': 10, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 7, + assert_finalizationstate(proposer, {'currentDynasty': 6, + 'currentEpoch': 8, + 'lastJustifiedEpoch': 6, + 'lastFinalizedEpoch': 6, 'validators': 1}) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) generate_block(proposer) - assert_finalizationstate(proposer, {'currentDynasty': 7, - 'currentEpoch': 10, - 'lastJustifiedEpoch': 9, - 'lastFinalizedEpoch': 8, + assert_finalizationstate(proposer, {'currentDynasty': 6, + 'currentEpoch': 8, + 'lastJustifiedEpoch': 7, + 'lastFinalizedEpoch': 7, 'validators': 1}) # finalizer1 can't vote so we keep it connected @@ -179,11 +180,11 @@ def run_test(self): assert_equal(len(proposer.getrawmempool()), 0) generate_block(proposer, count=3) - assert_equal(proposer.getblockcount(), 50) - assert_finalizationstate(proposer, {'currentDynasty': 7, - 'currentEpoch': 10, - 'lastJustifiedEpoch': 9, - 'lastFinalizedEpoch': 8, + assert_equal(proposer.getblockcount(), 40) + assert_finalizationstate(proposer, {'currentDynasty': 6, + 'currentEpoch': 8, + 'lastJustifiedEpoch': 7, + 'lastFinalizedEpoch': 7, 'validators': 1}) assert_equal(finalizer1.getvalidatorinfo()['validator_status'], 'WAITING_FOR_WITHDRAW_DELAY') assert_raises_rpc_error(-25, @@ -197,11 +198,11 @@ def run_test(self): self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) generate_block(proposer, count=4) - assert_equal(proposer.getblockcount(), 105) - assert_finalizationstate(proposer, {'currentDynasty': 18, - 'currentEpoch': 21, - 'lastJustifiedEpoch': 20, - 'lastFinalizedEpoch': 19, + assert_equal(proposer.getblockcount(), 95) + assert_finalizationstate(proposer, {'currentDynasty': 17, + 'currentEpoch': 19, + 'lastJustifiedEpoch': 18, + 'lastFinalizedEpoch': 18, 'validators': 1}) # last block that finalizer1 can't withdraw @@ -219,31 +220,33 @@ def run_test(self): self.log.info('finalizer1 could not withdraw during WITHDRAW_DELAY period') # test that deposit can be withdrawn - # e0 - e1 - ... - e6 - ... - e22[106, 107] - # d1 l1 w1 + # e0 - e1 - ... - e4 - ... - e20[95, 96, 97] + # d1 l1 w1 # d2 generate_block(proposer) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) - assert_equal(proposer.getblockcount(), 106) - assert_finalizationstate(proposer, {'currentDynasty': 19, - 'currentEpoch': 22, - 'lastJustifiedEpoch': 20, - 'lastFinalizedEpoch': 19, + assert_equal(proposer.getblockcount(), 96) + assert_finalizationstate(proposer, {'currentDynasty': 18, + 'currentEpoch': 20, + 'lastJustifiedEpoch': 18, + 'lastFinalizedEpoch': 18, 'validators': 1}) sync_blocks([proposer, finalizer1], timeout=10) assert_equal(finalizer1.getvalidatorinfo()['validator_status'], 'WAITING_TO_WITHDRAW') + assert_equal(finalizer1.getbalance(), Decimal('9999.99993840')) w1 = finalizer1.withdraw(finalizer1_address) wait_until(lambda: w1 in proposer.getrawmempool(), timeout=10) generate_block(proposer) sync_blocks([proposer, finalizer1]) assert_equal(finalizer1.getvalidatorinfo()['validator_status'], 'NOT_VALIDATING') + assert_equal(finalizer1.getbalance(), Decimal('9999.99992140')) self.log.info('finalizer1 was able to withdraw deposit at dynasty=18') # test that withdraw commit can be spent # test that deposit can be withdrawn - # e0 - e1 - ... - e6 - ... - e22[106, 107, 108] - # d1 l1 w1 spent_w1 + # e0 - e1 - ... - e4 - ... - e20[95, 96, 97, 98] + # d1 l1 w1 spent_w1 # d2 spent_w1_raw = finalizer1.createrawtransaction( [{'txid': w1, 'vout': 0}], {finalizer1_address: Decimal('1499.999')}) @@ -259,7 +262,7 @@ def run_test(self): # Test that after withdraw the node can deposit again sync_blocks([proposer, finalizer1], timeout=10) - assert_equal(proposer.getblockcount(), 108) + assert_equal(proposer.getblockcount(), 98) assert_equal(finalizer1.getvalidatorinfo()['validator_status'], 'NOT_VALIDATING') deposit = finalizer1.deposit(finalizer1.getnewaddress('', 'legacy'), 1500) assert_equal(finalizer1.getvalidatorinfo()['validator_status'], 'WAITING_DEPOSIT_CONFIRMATION') @@ -267,7 +270,7 @@ def run_test(self): self.wait_for_transaction(deposit, timeout=10, nodes=[proposer, finalizer1]) proposer.generate(1) sync_blocks([proposer, finalizer1], timeout=10) - assert_equal(proposer.getblockcount(), 109) + assert_equal(proposer.getblockcount(), 99) assert_equal(finalizer1.getvalidatorinfo()['validator_status'], 'WAITING_DEPOSIT_FINALIZATION') @@ -278,24 +281,15 @@ def run_test(self): proposer.generate(2) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) - assert_equal(proposer.getblockcount(), 111) + assert_equal(proposer.getblockcount(), 101) proposer.generate(5) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) - assert_equal(proposer.getblockcount(), 116) - assert_finalizationstate(proposer, {'currentDynasty': 21, - 'currentEpoch': 24, - 'lastJustifiedEpoch': 22, - 'lastFinalizedEpoch': 21, - 'validators': 1}) - - proposer.generate(5) - self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=proposer) - assert_equal(proposer.getblockcount(), 121) - assert_finalizationstate(proposer, {'currentDynasty': 22, - 'currentEpoch': 25, - 'lastJustifiedEpoch': 23, - 'lastFinalizedEpoch': 22, + assert_equal(proposer.getblockcount(), 106) + assert_finalizationstate(proposer, {'currentDynasty': 20, + 'currentEpoch': 22, + 'lastJustifiedEpoch': 20, + 'lastFinalizedEpoch': 20, 'validators': 2}) self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=proposer) diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py index bec19477f7..69bed201d2 100755 --- a/test/functional/mempool_resurrect.py +++ b/test/functional/mempool_resurrect.py @@ -33,10 +33,10 @@ def run_test(self): # Make the first 3 coinbase mature now node.generate(102) assert_equal(node.getblockcount(), 105) - assert_finalizationstate(node, {'currentDynasty': 18, + assert_finalizationstate(node, {'currentDynasty': 19, 'currentEpoch': 21, 'lastJustifiedEpoch': 20, - 'lastFinalizedEpoch': 19}) + 'lastFinalizedEpoch': 20}) node0_address = node.getnewaddress("", "bech32") # Spend block 1/2/3's coinbase transactions diff --git a/test/functional/p2p_commits.py b/test/functional/p2p_commits.py index 40a712a7fe..2edf96a669 100755 --- a/test/functional/p2p_commits.py +++ b/test/functional/p2p_commits.py @@ -99,10 +99,10 @@ def check_commits(status, hashes): generate(19) # When no validators present, node automatically justifies previous epoch. # So that: - # F F F J + # F F F F # 0 - 1..5 - 6..10 - 11..15 - 16..19 assert_equal(node.getblockcount(), 19) - assert_equal(node.getfinalizationstate()['lastFinalizedEpoch'], 2) + assert_equal(node.getfinalizationstate()['lastFinalizedEpoch'], 3) assert_equal(node.getfinalizationstate()['lastJustifiedEpoch'], 3) getcommits([blocks[4]]) # expect error: not a checkpoint @@ -112,43 +112,39 @@ def check_commits(status, hashes): getcommits([blocks[5]]) check_commits(0, blocks[6:11]) - getcommits([blocks[5], blocks[10]]) - check_commits(1, blocks[11:20]) + getcommits([blocks[10], blocks[15]]) + check_commits(1, blocks[16:20]) - getcommits([blocks[5], blocks[12]]) - check_commits(1, blocks[13:20]) + getcommits([blocks[15], blocks[16]]) + check_commits(1, blocks[17:20]) - getcommits([blocks[5], blocks[10], blocks[11]]) - check_commits(1, blocks[12:20]) + getcommits([blocks[10], blocks[15], blocks[16]]) + check_commits(1, blocks[17:20]) # ascend ordering is broken, 11 is considered biggest - getcommits([blocks[5], blocks[11], blocks[10]]) - check_commits(1, blocks[12:20]) + getcommits([blocks[10], blocks[16], blocks[15]]) + check_commits(1, blocks[17:20]) - # ascend ordering is broken, 11 is considered biggest, 12 is shadowed - getcommits([blocks[5], blocks[11], blocks[10], blocks[12]]) - check_commits(1, blocks[12:20]) + # ascend ordering is broken, 16 is considered biggest, 17 is shadowed + getcommits([blocks[10], blocks[16], blocks[15], blocks[17]]) + check_commits(1, blocks[17:20]) generate(1) # 19th block, non-finalized checkpoint assert_equal(node.getblockcount(), 20) - assert_equal(node.getfinalizationstate()['lastFinalizedEpoch'], 2) + assert_equal(node.getfinalizationstate()['lastFinalizedEpoch'], 3) assert_equal(node.getfinalizationstate()['lastJustifiedEpoch'], 3) - getcommits([blocks[15]]) # expect error + getcommits([blocks[16]]) # expect error: not finalized checkpoint time.sleep(2) assert_equal(len(p2p.messages), 0) # last epoch is full but still not finalized, expect status=1 - getcommits([blocks[10]]) - check_commits(1, blocks[11:21]) - - getcommits([blocks[15]]) # expect error: not finalized checkpoint - time.sleep(2) - assert_equal(len(p2p.messages), 0) + getcommits([blocks[15]]) + check_commits(1, blocks[16:21]) generate(1) assert_equal(node.getblockcount(), 21) - assert_equal(node.getfinalizationstate()['lastFinalizedEpoch'], 3) + assert_equal(node.getfinalizationstate()['lastFinalizedEpoch'], 4) assert_equal(node.getfinalizationstate()['lastJustifiedEpoch'], 4) # Epoch 16..20 is now finalized, expect status=0 @@ -156,11 +152,14 @@ def check_commits(status, hashes): check_commits(0, blocks[11:16]) getcommits([blocks[15]]) - check_commits(1, blocks[16:22]) + check_commits(0, blocks[16:21]) + + getcommits([blocks[20]]) + check_commits(1, blocks[21:22]) - # Ask for unknown block hash, check most recent block is 14. - getcommits([blocks[10], blocks[15], 0x4242424242]) - check_commits(1, blocks[16:22]) + # Ask for unknown block hash, check most recent block is 20. + getcommits([blocks[15], blocks[20], 0x4242424242]) + check_commits(1, blocks[21:22]) def commits_test(self, node): def check_headers(number): diff --git a/test/functional/p2p_snapshot.py b/test/functional/p2p_snapshot.py index c941eb36e7..b0d984b8d1 100755 --- a/test/functional/p2p_snapshot.py +++ b/test/functional/p2p_snapshot.py @@ -383,9 +383,9 @@ def test_sync_with_restarts(self): self.setup_stake_coins(snap_node) - # generate 2 epochs + 1 block to create the first finalized snapshot - generate_block(snap_node, count=5 + 5 + 1) - assert_equal(snap_node.getblockcount(), 11) + # generate 1 epoch + 1 block to create the first finalized snapshot + snap_node.generatetoaddress(5 + 1, snap_node.getnewaddress('', 'bech32')) + assert_equal(snap_node.getblockcount(), 6) wait_until(lambda: has_valid_snapshot(snap_node, 4), timeout=10) # configure p2p to have snapshot header and parent block @@ -476,19 +476,20 @@ def test_invalid_snapshot(self): self.setup_stake_coins(snap_node) - # generate 2 epochs + 1 block to create the first finalized snapshot + # generate 1 epoch + 1 block to create the first finalized snapshot # and store it in valid_p2p - generate_block(snap_node, count=5 + 5 + 1) - assert_equal(snap_node.getblockcount(), 11) + generate_block(snap_node, count=5 + 1) + assert_equal(snap_node.getblockcount(), 6) wait_until(lambda: has_valid_snapshot(snap_node, 4), timeout=10) valid_p2p = WaitNode() valid_p2p.update_snapshot_from(snap_node) # create the second snapshot and store it in broken_p2p - generate_block(snap_node, count=5) - assert_equal(snap_node.getblockcount(), 16) + generate_block(snap_node, count=9) + assert_equal(snap_node.getblockcount(), 15) wait_until(lambda: has_valid_snapshot(snap_node, 9), timeout=10) + wait_until(lambda: has_valid_snapshot(snap_node, 14), timeout=10) broken_p2p = WaitNode() broken_p2p.update_snapshot_from(snap_node) @@ -543,7 +544,7 @@ def test_invalid_snapshot(self): assert_equal(not_finalized_p2p.snapshot_chunk1_requested, False) # node requests parent block and finishes ISD - wait_until(lambda: node.getblockcount() == 16, timeout=20) + wait_until(lambda: node.getblockcount() == 15, timeout=20) node.disconnect_p2ps() assert_chainstate_equal(snap_node, node) diff --git a/test/functional/rpc_finalization.py b/test/functional/rpc_finalization.py index 5f4ef41213..036d27f586 100755 --- a/test/functional/rpc_finalization.py +++ b/test/functional/rpc_finalization.py @@ -96,7 +96,7 @@ def create_deposit(finalizer, node): self.log.info('finalization state includes new validators') # test instant justification 2 - # F J + # F F # e0 - e1 - e2 generate_block(node, count=5) assert_equal(node.getblockcount(), 10) @@ -105,241 +105,249 @@ def create_deposit(finalizer, node): assert_equal(state['currentDynastyStartsAtEpoch'], 1) assert_equal(state['currentEpoch'], 2) assert_equal(state['lastJustifiedEpoch'], 1) - assert_equal(state['lastFinalizedEpoch'], 0) + assert_equal(state['lastFinalizedEpoch'], 1) assert_equal(state['validators'], 0) self.log.info('instant finalization 1 is correct') - # test instant justification 3 - # F F J + # test instant justification 3 (last one) + # F F F # e0 - e1 - e2 - e3 generate_block(node, count=5) assert_equal(node.getblockcount(), 15) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 0) - assert_equal(state['currentDynastyStartsAtEpoch'], 1) + assert_equal(state['currentDynasty'], 1) + assert_equal(state['currentDynastyStartsAtEpoch'], 3) assert_equal(state['currentEpoch'], 3) assert_equal(state['lastJustifiedEpoch'], 2) - assert_equal(state['lastFinalizedEpoch'], 1) + assert_equal(state['lastFinalizedEpoch'], 2) assert_equal(state['validators'], 0) self.log.info('instant finalization 2 is correct') - # test instant justification 4 - # F F F J + # test that finalizer starts voting + # F F F F # e0 - e1 - e2 - e3 - e4 - generate_block(node, count=5) + generate_block(node) + assert_equal(node.getblockcount(), 16) + state = node.getfinalizationstate() + assert_equal(state['currentDynasty'], 2) + assert_equal(state['currentDynastyStartsAtEpoch'], 4) + assert_equal(state['currentEpoch'], 4) + assert_equal(state['lastJustifiedEpoch'], 2) + assert_equal(state['lastFinalizedEpoch'], 2) + assert_equal(state['validators'], 1) + + self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) + generate_block(node, count=4) assert_equal(node.getblockcount(), 20) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 1) + assert_equal(state['currentDynasty'], 2) assert_equal(state['currentDynastyStartsAtEpoch'], 4) assert_equal(state['currentEpoch'], 4) assert_equal(state['lastJustifiedEpoch'], 3) - assert_equal(state['lastFinalizedEpoch'], 2) - assert_equal(state['validators'], 0) - self.log.info('instant finalization 3 is correct') + assert_equal(state['lastFinalizedEpoch'], 3) + assert_equal(state['validators'], 1) + self.log.info('finalizer successfully voted and justified previous epoch') - # test instant justification 5 (must be last one) - # F F F F J + # test that finalizer is voting second time + # F F F F F # e0 - e1 - e2 - e3 - e4 - e5 - generate_block(node, count=5) + generate_block(node) + assert_equal(node.getblockcount(), 21) + state = node.getfinalizationstate() + assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynastyStartsAtEpoch'], 5) + assert_equal(state['currentEpoch'], 5) + assert_equal(state['lastJustifiedEpoch'], 3) + assert_equal(state['lastFinalizedEpoch'], 3) + assert_equal(state['validators'], 1) + + self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) + generate_block(node, count=4) assert_equal(node.getblockcount(), 25) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 2) + assert_equal(state['currentDynasty'], 3) assert_equal(state['currentDynastyStartsAtEpoch'], 5) assert_equal(state['currentEpoch'], 5) assert_equal(state['lastJustifiedEpoch'], 4) - assert_equal(state['lastFinalizedEpoch'], 3) - assert_equal(state['validators'], 0) + assert_equal(state['lastFinalizedEpoch'], 4) + assert_equal(state['validators'], 1) + self.log.info('finalizer successfully voted second time') # no justification - # F F F F J + # F F F F F # e0 - e1 - e2 - e3 - e4 - e5 - e6 generate_block(node) assert_equal(node.getblockcount(), 26) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynasty'], 4) assert_equal(state['currentDynastyStartsAtEpoch'], 6) assert_equal(state['currentEpoch'], 6) assert_equal(state['lastJustifiedEpoch'], 4) - assert_equal(state['lastFinalizedEpoch'], 3) + assert_equal(state['lastFinalizedEpoch'], 4) assert_equal(state['validators'], 1) generate_block(node, count=4) assert_equal(node.getblockcount(), 30) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynasty'], 4) assert_equal(state['currentDynastyStartsAtEpoch'], 6) assert_equal(state['currentEpoch'], 6) assert_equal(state['lastJustifiedEpoch'], 4) - assert_equal(state['lastFinalizedEpoch'], 3) + assert_equal(state['lastFinalizedEpoch'], 4) assert_equal(state['validators'], 1) # no justification - # F F F F J + # F F F F F # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7[31] generate_block(node) assert_equal(node.getblockcount(), 31) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynasty'], 4) assert_equal(state['currentDynastyStartsAtEpoch'], 6) assert_equal(state['currentEpoch'], 7) assert_equal(state['lastJustifiedEpoch'], 4) - assert_equal(state['lastFinalizedEpoch'], 3) + assert_equal(state['lastFinalizedEpoch'], 4) assert_equal(state['validators'], 1) self.log.info('finalization state without justification is correct') # create first justification - # F F F F J J + # F F F F F J # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7[31, 32] self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) generate_block(node) assert_equal(node.getblockcount(), 32) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynasty'], 4) assert_equal(state['currentDynastyStartsAtEpoch'], 6) assert_equal(state['currentEpoch'], 7) assert_equal(state['lastJustifiedEpoch'], 6) - assert_equal(state['lastFinalizedEpoch'], 3) + assert_equal(state['lastFinalizedEpoch'], 4) assert_equal(state['validators'], 1) self.log.info('finalization state after justification is correct') # skip 1 justification - # F F F F J J + # F F F F F J # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9[41] generate_block(node, count=9) assert_equal(node.getblockcount(), 41) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynasty'], 4) assert_equal(state['currentDynastyStartsAtEpoch'], 6) assert_equal(state['currentEpoch'], 9) assert_equal(state['lastJustifiedEpoch'], 6) - assert_equal(state['lastFinalizedEpoch'], 3) + assert_equal(state['lastFinalizedEpoch'], 4) assert_equal(state['validators'], 1) self.log.info('finalization state without justification is correct') # create finalization - # F F F J J J + # F F F F F J J # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9[41, 42] self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) generate_block(node) assert_equal(node.getblockcount(), 42) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynasty'], 4) assert_equal(state['currentDynastyStartsAtEpoch'], 6) assert_equal(state['currentEpoch'], 9) assert_equal(state['lastJustifiedEpoch'], 8) - assert_equal(state['lastFinalizedEpoch'], 3) + assert_equal(state['lastFinalizedEpoch'], 4) assert_equal(state['validators'], 1) - # F F F F J J F J + # F F F F F J J F # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10[46, 47] generate_block(node, count=4) self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) generate_block(node) assert_equal(node.getblockcount(), 47) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynasty'], 4) assert_equal(state['currentDynastyStartsAtEpoch'], 6) assert_equal(state['currentEpoch'], 10) assert_equal(state['lastJustifiedEpoch'], 9) - assert_equal(state['lastFinalizedEpoch'], 8) + assert_equal(state['lastFinalizedEpoch'], 9) assert_equal(state['validators'], 1) - # F F F F J J F J + # F F F F F J J F # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 generate_block(node, count=3) assert_equal(node.getblockcount(), 50) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 3) + assert_equal(state['currentDynasty'], 4) assert_equal(state['currentDynastyStartsAtEpoch'], 6) assert_equal(state['currentEpoch'], 10) assert_equal(state['lastJustifiedEpoch'], 9) - assert_equal(state['lastFinalizedEpoch'], 8) + assert_equal(state['lastFinalizedEpoch'], 9) assert_equal(state['validators'], 1) self.log.info('finalization state after finalization is correct') - # F F F F J J F J + # F F F F F J J F # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 - e11[51] generate_block(node) assert_equal(node.getblockcount(), 51) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 4) + assert_equal(state['currentDynasty'], 5) assert_equal(state['currentDynastyStartsAtEpoch'], 11) assert_equal(state['currentEpoch'], 11) assert_equal(state['lastJustifiedEpoch'], 9) - assert_equal(state['lastFinalizedEpoch'], 8) + assert_equal(state['lastFinalizedEpoch'], 9) assert_equal(state['validators'], 1) self.log.info('dynasty after finalization is updated correctly') - # add finalizer2 deposit at dynasty=5. will vote at dynasty=8 + # add finalizer2 deposit at dynasty=5. will vote at dynasty=7 create_deposit(finalizer2, node) - # F F F F J J F F J + # F F F F F J J F F # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 - e11 self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) generate_block(node, count=4) assert_equal(node.getblockcount(), 55) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 4) + assert_equal(state['currentDynasty'], 5) assert_equal(state['currentDynastyStartsAtEpoch'], 11) assert_equal(state['currentEpoch'], 11) assert_equal(state['lastJustifiedEpoch'], 10) - assert_equal(state['lastFinalizedEpoch'], 9) + assert_equal(state['lastFinalizedEpoch'], 10) assert_equal(state['validators'], 1) - # F F F F J J F F F J + # F F F F F J J F F F # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 - e11 - e12 generate_block(node) self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) generate_block(node, count=4) assert_equal(node.getblockcount(), 60) state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 5) + assert_equal(state['currentDynasty'], 6) assert_equal(state['currentDynastyStartsAtEpoch'], 12) assert_equal(state['currentEpoch'], 12) assert_equal(state['lastJustifiedEpoch'], 11) - assert_equal(state['lastFinalizedEpoch'], 10) - assert_equal(state['validators'], 1) - - # F F F J J F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 - e11 - e12 - e13 - generate_block(node) - self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) - generate_block(node, count=4) - assert_equal(node.getblockcount(), 65) - state = node.getfinalizationstate() - assert_equal(state['currentDynasty'], 6) - assert_equal(state['currentDynastyStartsAtEpoch'], 13) - assert_equal(state['currentEpoch'], 13) - assert_equal(state['lastJustifiedEpoch'], 12) assert_equal(state['lastFinalizedEpoch'], 11) assert_equal(state['validators'], 1) - # F F F F J J F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 - e11 - e12 - e13 - e14[66] + # F F F F F J J F F F + # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 - e11 - e12 - e13[61] generate_block(node) - assert_equal(node.getblockcount(), 66) + assert_equal(node.getblockcount(), 61) state = node.getfinalizationstate() assert_equal(state['currentDynasty'], 7) - assert_equal(state['currentDynastyStartsAtEpoch'], 14) - assert_equal(state['currentEpoch'], 14) - assert_equal(state['lastJustifiedEpoch'], 12) + assert_equal(state['currentDynastyStartsAtEpoch'], 13) + assert_equal(state['currentEpoch'], 13) + assert_equal(state['lastJustifiedEpoch'], 11) assert_equal(state['lastFinalizedEpoch'], 11) assert_equal(state['validators'], 2) self.log.info('new deposit was activated correctly') - # F F F F J J F F F F F J - # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 - e11 - e12 - e13 - e14 + # F F F F F J J F F F F + # e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7 - e8 - e9 - e10 - e11 - e12 - e13 self.wait_for_vote_and_disconnect(finalizer=finalizer1, node=node) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=node) generate_block(node, count=4) - assert_equal(node.getblockcount(), 70) + assert_equal(node.getblockcount(), 65) state = node.getfinalizationstate() assert_equal(state['currentDynasty'], 7) - assert_equal(state['currentDynastyStartsAtEpoch'], 14) - assert_equal(state['currentEpoch'], 14) - assert_equal(state['lastJustifiedEpoch'], 13) + assert_equal(state['currentEpoch'], 13) + assert_equal(state['lastJustifiedEpoch'], 12) assert_equal(state['lastFinalizedEpoch'], 12) assert_equal(state['validators'], 2) self.log.info('new finalizer votes') diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py index 134e9a47b8..997e4ee876 100755 --- a/test/functional/rpc_preciousblock.py +++ b/test/functional/rpc_preciousblock.py @@ -6,7 +6,7 @@ from io import BytesIO -from test_framework.test_framework import UnitETestFramework +from test_framework.test_framework import UnitETestFramework, DISABLE_FINALIZATION from test_framework.util import ( assert_equal, connect_nodes_bi, @@ -49,6 +49,11 @@ class PreciousTest(UnitETestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 + self.extra_args = [ + [DISABLE_FINALIZATION], + [DISABLE_FINALIZATION], + [DISABLE_FINALIZATION], + ] def skip_test_if_missing_module(self): self.skip_if_no_wallet() From c083aa1861c22f0e7b7bc8d7cf745c84e46f9ddb Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Wed, 8 May 2019 16:19:15 +0200 Subject: [PATCH 02/11] Leave comments to not obvious calculations in the code Signed-off-by: Kostiantyn Stepaniuk --- src/Makefile.test.include | 2 +- src/esperanza/finalizationstate.cpp | 19 ++++++++++++++++--- ...nstate_calculate_withdraw_amount_tests.cpp | 1 - .../esperanza/finalizationstate_utils.cpp | 3 ++- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 7066ad89fe..b26329aa63 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -74,8 +74,8 @@ UNITE_TESTS =\ test/esperanza/admincommand_tests.cpp \ test/esperanza/adminstate_tests.cpp \ test/esperanza/checks_tests.cpp \ - test/esperanza/finalizationstate_tests.cpp \ test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp \ + test/esperanza/finalizationstate_tests.cpp \ test/esperanza/finalizationstate_deposit_tests.cpp \ test/esperanza/finalizationstate_vote_tests.cpp \ test/esperanza/finalizationstate_logout_tests.cpp \ diff --git a/src/esperanza/finalizationstate.cpp b/src/esperanza/finalizationstate.cpp index 1d98f611b7..e612961ce5 100644 --- a/src/esperanza/finalizationstate.cpp +++ b/src/esperanza/finalizationstate.cpp @@ -125,6 +125,13 @@ Result FinalizationState::InitializeEpoch(blockchain::Height blockHeight) { InstaJustify(); } + // new source is already known when the checkpoint is processed but we didn't + // update it as the previous one is used inside of this function. To not keep + // current and previous expected source in the FinalizationState, we update + // it here and the limitation of it is that we can't create vote inside the + // first block of new epoch as new source is not known yet for finalizers. + m_expected_source_epoch = m_last_justified_epoch; + std::string log_msg; if (m_current_epoch >= 2 && m_last_justified_epoch != m_current_epoch - 2) { log_msg = " epoch=" + std::to_string(m_current_epoch - 2) + " was not justified."; @@ -195,12 +202,19 @@ ufp64::ufp64_t FinalizationState::GetCollectiveRewardFactor() { return 0; } + // we use "m_current_epoch - 2" epoch to retrieve dynasty votes because: + // -1 as m_current_epoch is already incremented inside of InitializeEpoch + // before this function is invoked + // -1 because when we store votes we use vote.m_target_epoch which is always + // one below the m_current_epoch + const uint32_t checkpoint_epoch = m_current_epoch - 2; + ufp64::ufp64_t curVoteFraction = ufp64::div_2uint( - GetCheckpoint(m_current_epoch - 2).GetCurDynastyVotes(m_expected_source_epoch - 1), + GetCheckpoint(checkpoint_epoch).GetCurDynastyVotes(m_expected_source_epoch), m_cur_dyn_deposits); ufp64::ufp64_t prevVoteFraction = ufp64::div_2uint( - GetCheckpoint(m_current_epoch - 2).GetPrevDynastyVotes(m_expected_source_epoch - 1), + GetCheckpoint(checkpoint_epoch).GetPrevDynastyVotes(m_expected_source_epoch), m_prev_dyn_deposits); ufp64::ufp64_t voteFraction = ufp64::min(curVoteFraction, prevVoteFraction); @@ -1020,7 +1034,6 @@ void FinalizationState::ProcessNewCommits(const CBlockIndex &block_index, m_recommended_target_hash = block_index.GetBlockHash(); m_recommended_target_epoch = GetEpoch(block_index); - m_expected_source_epoch = m_last_justified_epoch; } m_status = FROM_COMMITS; } diff --git a/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp b/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp index 014e58c69d..7d269dce06 100644 --- a/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp +++ b/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp @@ -15,7 +15,6 @@ void CreateAndProcessVote(FinalizationStateSpy &state, uint160 finalizer_address } void InitializeNextEpoch(FinalizationStateSpy &state) { - state.SetExpectedSourceEpoch(state.GetLastJustifiedEpoch()); state.SetRecommendedTargetEpoch(state.GetCurrentEpoch()); Result res = state.InitializeEpoch(1 + state.GetCurrentEpoch() * state.EpochLength()); diff --git a/src/test/esperanza/finalizationstate_utils.cpp b/src/test/esperanza/finalizationstate_utils.cpp index fa13673d3d..7f04c5bd9d 100644 --- a/src/test/esperanza/finalizationstate_utils.cpp +++ b/src/test/esperanza/finalizationstate_utils.cpp @@ -96,7 +96,8 @@ void FinalizationStateSpy::CreateAndActivateDeposit(const uint160 &validator_add for (uint32_t i = 1; i < 4 * EpochLength() + 1; i += EpochLength()) { BOOST_REQUIRE_EQUAL(GetActiveFinalizers().size(), 0); - m_expected_source_epoch = m_last_justified_epoch; + // recommended target epoch in ProcessNewCommits + // when checkpoint is being processed m_recommended_target_epoch = m_current_epoch; res = InitializeEpoch(i); From f26af2367212a83ddc6d0cc6c4d1fbd17f34890c Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Wed, 8 May 2019 17:44:13 +0200 Subject: [PATCH 03/11] Fix floating finalization_slash_itself.py Signed-off-by: Kostiantyn Stepaniuk --- test/functional/finalization_slash_itself.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/functional/finalization_slash_itself.py b/test/functional/finalization_slash_itself.py index 95d47e92cc..8ba5dacb89 100755 --- a/test/functional/finalization_slash_itself.py +++ b/test/functional/finalization_slash_itself.py @@ -138,10 +138,7 @@ def test_double_votes(self): 'lastFinalizedEpoch': 3, 'validators': 1}) - connect_nodes(finalizer2, fork2.index) - wait_until(lambda: len(finalizer2.getrawmempool()) == 1, timeout=10) - sync_mempools([fork2, finalizer2]) - assert_equal(len(fork2.getrawmempool()), 1) + self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=fork2) # check that a vote, and not a slash is actually in the mempool vote = fork2.decoderawtransaction(fork2.getrawtransaction(fork2.getrawmempool()[0])) @@ -149,7 +146,6 @@ def test_double_votes(self): fork2.generatetoaddress(1, fork1.getnewaddress('', 'bech32')) assert_equal(len(fork2.getrawmempool()), 0) - disconnect_nodes(finalizer2, fork2.index) # check if there is slashing after voting fork2.generatetoaddress(3, fork1.getnewaddress('', 'bech32')) From b0819180a654464cc51706d2670b4643af4f1132 Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Thu, 9 May 2019 14:14:28 +0200 Subject: [PATCH 04/11] Test CalculateWithdrawAmount when finalizer sometimes votes Signed-off-by: Kostiantyn Stepaniuk --- ...nstate_calculate_withdraw_amount_tests.cpp | 157 +++++++++++++++++- .../esperanza/finalizationstate_utils.cpp | 15 +- src/test/esperanza/finalizationstate_utils.h | 1 + 3 files changed, 163 insertions(+), 10 deletions(-) diff --git a/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp b/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp index 7d269dce06..ce39c71023 100644 --- a/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp +++ b/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp @@ -9,9 +9,7 @@ BOOST_FIXTURE_TEST_SUITE(finalizationstate_calculate_withdraw_amount_tests, Test void CreateAndProcessVote(FinalizationStateSpy &state, uint160 finalizer_address, uint256 target_hash) { Vote vote{finalizer_address, target_hash, state.GetExpectedSourceEpoch(), state.GetRecommendedTargetEpoch()}; BOOST_REQUIRE_EQUAL(state.ValidateVote(vote), +Result::SUCCESS); - uint32_t prev_justified = state.GetLastJustifiedEpoch(); state.ProcessVote(vote); - BOOST_REQUIRE_EQUAL(state.GetLastJustifiedEpoch(), prev_justified + 1); } void InitializeNextEpoch(FinalizationStateSpy &state) { @@ -21,8 +19,12 @@ void InitializeNextEpoch(FinalizationStateSpy &state) { BOOST_REQUIRE_EQUAL(res, +Result::SUCCESS); } -BOOST_AUTO_TEST_CASE(calculate_withdraw_amount) { +BOOST_AUTO_TEST_CASE(calculate_withdraw_amount_always_voting) { + // This test creates one finalizer which always votes and justifies epochs. + // Each test case configures how many epochs the finalizer votes before logout + // and asserts the withdrawal amount. struct TestCase { + std::string comment; uint32_t epochs_before_logout; CAmount deposit_amount; CAmount withdraw_amount; @@ -30,21 +32,25 @@ BOOST_AUTO_TEST_CASE(calculate_withdraw_amount) { std::vector test_cases{ TestCase{ + "logout right away", 0, 1000000000000, 1000011069779, }, TestCase{ + "vote once and logout", 1, 1000000000000, 1000014759724, }, TestCase{ + "vote 10 times and logout", 10, 1000000000000, 1000047969221, }, TestCase{ + "vote 100 times and logout", 100, 1000000000000, 1000380063832, @@ -106,7 +112,150 @@ BOOST_AUTO_TEST_CASE(calculate_withdraw_amount) { // test amount BOOST_REQUIRE_EQUAL(state.ValidateWithdraw(finalizer_address, test_case.deposit_amount), +Result::SUCCESS); - BOOST_REQUIRE_EQUAL(state.GetLastFinalizedEpoch(), state.GetCurrentEpoch() - 1); + BOOST_REQUIRE_EQUAL(state.GetLastFinalizedEpoch(), state.GetCurrentEpoch() - 1); // last one insta-justified + + for (uint32_t i = 0; i < 3; ++i) { + CAmount amount; + BOOST_CHECK_MESSAGE(state.CalculateWithdrawAmount(finalizer_address, amount) == +esperanza::Result::SUCCESS, + strprintf("test_case=%i: loop=%i: cannot calculate withdraw amount", test_idx, i)); + BOOST_CHECK_MESSAGE(amount == test_case.withdraw_amount, + strprintf("test_case=%i: loop=%i: amount: expected=%d received=%d", + test_idx, + i, + test_case.withdraw_amount, + amount)); + InitializeNextEpoch(state); + } + } +} + +BOOST_AUTO_TEST_CASE(calculate_withdraw_amount_sometimes_voting) { + // This test creates the `finalizer_address` finalizer which logouts after + // `epochs_before_logout` epochs and votes in first `vote_in_epochs` epochs. + // However, in every epoch finalization is reached as there is a second + // `large_finalizer_address` finalizer that holds the majority of deposits. + struct TestCase { + std::string comment; + uint32_t epochs_before_logout; + uint32_t vote_in_epochs; + CAmount deposit_amount; + CAmount withdraw_amount; + }; + + std::vector test_cases{ + TestCase{ + "logout after 50 epochs. Don't vote", + 50, + 0, + 1000000000000, + 999868240000, + }, + TestCase{ + "logout after 50 epochs. Vote in first 10 epochs", + 50, + 10, + 1000000000000, + 999903276441, + }, + TestCase{ + "logout after 50 epochs. Vote in first 20 epochs", + 50, + 20, + 1000000000000, + 999947073698, + }, + TestCase{ + "logout after 50 epochs. Vote in first 30 epochs", + 50, + 30, + 1000000000000, + 999990872849, + }, + TestCase{ + "logout after 50 epochs. Vote in first 40 epochs", + 50, + 40, + 1000000000000, + 1000034673893, + }, + TestCase{ + "logout after 50 epochs. Vote in all epochs", + 50, + 50, + 1000000000000, + 1000078476834, + }, + }; + + for (size_t test_idx = 0; test_idx < test_cases.size(); ++test_idx) { + TestCase test_case = test_cases[test_idx]; + BOOST_REQUIRE(test_case.epochs_before_logout >= test_case.vote_in_epochs); + + // setup + finalization::Params params = finalization::Params::TestNet(); + FinalizationStateSpy state(params); + + // mock target hash + uint256 target_hash = GetRandHash(); + CBlockIndex block_index; + block_index.phashBlock = &target_hash; + state.SetRecommendedTarget(block_index); + + // deposit + uint160 large_finalizer_address = RandValidatorAddr(); + state.CreateDeposit(large_finalizer_address, test_case.deposit_amount * 3); + + uint160 finalizer_address = RandValidatorAddr(); + state.CreateAndActivateDeposit(finalizer_address, test_case.deposit_amount); + BOOST_REQUIRE_EQUAL(state.GetActiveFinalizers().size(), 2); + + // vote before logout + uint32_t end = state.GetCurrentEpoch() + test_case.epochs_before_logout; + uint32_t vote_until = state.GetCurrentEpoch() + test_case.vote_in_epochs; + for (uint32_t i = state.GetCurrentEpoch(); i < end; ++i) { + if (i < vote_until) { + CreateAndProcessVote(state, finalizer_address, target_hash); + } + + CreateAndProcessVote(state, large_finalizer_address, target_hash); + InitializeNextEpoch(state); + } + + // logout + BOOST_REQUIRE_EQUAL(state.ValidateLogout(finalizer_address), +Result::SUCCESS); + state.ProcessLogout(finalizer_address); + BOOST_REQUIRE_EQUAL(state.GetCurrentEpoch(), 4 + test_case.epochs_before_logout); + BOOST_REQUIRE_EQUAL(state.GetCurrentDynasty(), 2 + test_case.epochs_before_logout); + + // pass logout delay + uint32_t end_logout = state.GetCurrentEpoch() + static_cast(state.DynastyLogoutDelay()); + BOOST_REQUIRE_EQUAL(end_logout, 9 + test_case.epochs_before_logout); + + const esperanza::Validator *finalizer = state.GetValidator(finalizer_address); + BOOST_REQUIRE(finalizer); + + for (uint32_t i = state.GetCurrentEpoch(); i <= end_logout; ++i) { + CreateAndProcessVote(state, large_finalizer_address, target_hash); + InitializeNextEpoch(state); + BOOST_REQUIRE_EQUAL(state.GetLastFinalizedEpoch(), state.GetCurrentEpoch() - 2); + } + + // wait withdraw delay + uint32_t end_withdraw = end_logout + 1 + static_cast(state.WithdrawalEpochDelay()); + BOOST_REQUIRE_EQUAL(end_withdraw, 20 + test_case.epochs_before_logout); + + for (uint32_t i = state.GetCurrentEpoch(); i < end_withdraw; ++i) { + CreateAndProcessVote(state, large_finalizer_address, target_hash); + + BOOST_REQUIRE_EQUAL(state.ValidateWithdraw(finalizer_address, test_case.deposit_amount), +Result::WITHDRAW_TOO_EARLY); + CAmount amount; + BOOST_REQUIRE_EQUAL(state.CalculateWithdrawAmount(finalizer_address, amount), +Result::WITHDRAW_TOO_EARLY); + InitializeNextEpoch(state); + BOOST_REQUIRE_EQUAL(state.GetLastFinalizedEpoch(), state.GetCurrentEpoch() - 2); + } + + // test amount + BOOST_REQUIRE_EQUAL(state.ValidateWithdraw(finalizer_address, test_case.deposit_amount), +Result::SUCCESS); for (uint32_t i = 0; i < 3; ++i) { CAmount amount; diff --git a/src/test/esperanza/finalizationstate_utils.cpp b/src/test/esperanza/finalizationstate_utils.cpp index 7f04c5bd9d..9847a26ce0 100644 --- a/src/test/esperanza/finalizationstate_utils.cpp +++ b/src/test/esperanza/finalizationstate_utils.cpp @@ -88,10 +88,7 @@ void FinalizationStateSpy::shuffle() { void FinalizationStateSpy::CreateAndActivateDeposit(const uint160 &validator_address, CAmount deposit_size) { BOOST_REQUIRE_EQUAL(GetCurrentEpoch(), 0); - Result res = ValidateDeposit(validator_address, deposit_size); - BOOST_REQUIRE_EQUAL(res, +Result::SUCCESS); - - ProcessDeposit(validator_address, deposit_size); + CreateDeposit(validator_address, deposit_size); for (uint32_t i = 1; i < 4 * EpochLength() + 1; i += EpochLength()) { BOOST_REQUIRE_EQUAL(GetActiveFinalizers().size(), 0); @@ -100,7 +97,7 @@ void FinalizationStateSpy::CreateAndActivateDeposit(const uint160 &validator_add // when checkpoint is being processed m_recommended_target_epoch = m_current_epoch; - res = InitializeEpoch(i); + Result res = InitializeEpoch(i); BOOST_REQUIRE_EQUAL(res, +Result::SUCCESS); } @@ -108,7 +105,13 @@ void FinalizationStateSpy::CreateAndActivateDeposit(const uint160 &validator_add BOOST_REQUIRE_EQUAL(GetCurrentEpoch(), 4); BOOST_REQUIRE_EQUAL(GetLastJustifiedEpoch(), 2); BOOST_REQUIRE_EQUAL(GetLastFinalizedEpoch(), 2); - BOOST_REQUIRE_EQUAL(GetActiveFinalizers().size(), 1); + BOOST_REQUIRE(!GetActiveFinalizers().empty()); BOOST_REQUIRE_EQUAL(m_expected_source_epoch, 2); BOOST_REQUIRE_EQUAL(m_recommended_target_epoch, 3); } + +void FinalizationStateSpy::CreateDeposit(const uint160 &finalizer_address, CAmount deposit_size) { + Result res = ValidateDeposit(finalizer_address, deposit_size); + BOOST_REQUIRE_EQUAL(res, +Result::SUCCESS); + ProcessDeposit(finalizer_address, deposit_size); +} diff --git a/src/test/esperanza/finalizationstate_utils.h b/src/test/esperanza/finalizationstate_utils.h index 19ed79a367..f2fd5ca03f 100644 --- a/src/test/esperanza/finalizationstate_utils.h +++ b/src/test/esperanza/finalizationstate_utils.h @@ -51,6 +51,7 @@ class FinalizationStateSpy : public FinalizationState { } void CreateAndActivateDeposit(const uint160 &validator_address, CAmount deposit_size); + void CreateDeposit(const uint160 &finalizer_address, CAmount deposit_size); void shuffle(); From f765b51683f0e0c0ecaeb8f554f3783b1690264d Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Thu, 9 May 2019 14:59:27 +0200 Subject: [PATCH 05/11] Leave a comment why finalization is disabled Signed-off-by: Kostiantyn Stepaniuk --- src/esperanza/finalizationstate.cpp | 2 +- test/functional/rpc_preciousblock.py | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/esperanza/finalizationstate.cpp b/src/esperanza/finalizationstate.cpp index e612961ce5..12ea9a81fd 100644 --- a/src/esperanza/finalizationstate.cpp +++ b/src/esperanza/finalizationstate.cpp @@ -168,7 +168,7 @@ void FinalizationState::InstaJustify() { void FinalizationState::IncrementDynasty() { // finalized epoch is m_current_epoch - 2 because: - // justified(0) - finalized(1) - votes to justify(2) - m_current_epoch(3) + // finalized(0) - votes to justify(1) - m_current_epoch(2) // skip dynasty increment for the hardcoded finalized epoch=0 // as it's already "considered" incremented from -1 to 0. diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py index 997e4ee876..0459519318 100755 --- a/test/functional/rpc_preciousblock.py +++ b/test/functional/rpc_preciousblock.py @@ -49,11 +49,9 @@ class PreciousTest(UnitETestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 - self.extra_args = [ - [DISABLE_FINALIZATION], - [DISABLE_FINALIZATION], - [DISABLE_FINALIZATION], - ] + + # disable finalization as this test relies on reorgs before it + self.extra_args = [[DISABLE_FINALIZATION]] * self.num_nodes def skip_test_if_missing_module(self): self.skip_if_no_wallet() From 7dbf515494777c5ad5e0c761c29447a87014622a Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Thu, 9 May 2019 18:54:15 +0200 Subject: [PATCH 06/11] Disable test block Signed-off-by: Kostiantyn Stepaniuk --- test/functional/finalization_vote.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/test/functional/finalization_vote.py b/test/functional/finalization_vote.py index 4c01c66164..1c529e1bba 100755 --- a/test/functional/finalization_vote.py +++ b/test/functional/finalization_vote.py @@ -153,27 +153,25 @@ def run_test(self): prev_tx = finalizer1.decoderawtransaction(prev_tx)['txid'] # test that node recognizes old and invalid votes. - tx = make_vote_tx(finalizer1, address1, node0.getblockhash(40), 5, 8, prev_tx) - assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) - tx = make_vote_tx(finalizer1, address1, node0.getblockhash(40), 7, 9, prev_tx) - assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) - tx = make_vote_tx(finalizer1, address1, node0.getblockhash(40), 5, 6, prev_tx) - assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) - tx = make_vote_tx(finalizer1, address1, node0.getblockhash(40), 7, 6, prev_tx) - assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) + # tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 1, 2, prev_tx) + # assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) + # tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 2, 3, prev_tx) + # assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) + # tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 7, 6, prev_tx) + # assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) self.log.info('Tested outdated and invalid vote votes') # check that make_vote_tx works as expected (we really rely on this guy on tests above) - tx = make_vote_tx(finalizer1, address1, node0.getblockhash(40), 7, 8, prev_tx) + tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 5, 6, prev_tx) node0.sendrawtransaction(tx) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=node0) self.wait_for_vote_and_disconnect(finalizer=finalizer3, node=node0) generate_block(node0) - assert_equal(node0.getblockcount(), 45) - assert_finalizationstate(node0, {'currentDynasty': 6, - 'currentEpoch': 9, - 'lastJustifiedEpoch': 8, - 'lastFinalizedEpoch': 7, + assert_equal(node0.getblockcount(), 35) + assert_finalizationstate(node0, {'currentDynasty': 5, + 'currentEpoch': 7, + 'lastJustifiedEpoch': 6, + 'lastFinalizedEpoch': 6, 'validators': 3}) self.log.info('make_vote_tx works together with real finalizers') From aa7a2f7f29007a817a3cd6b90ba1d98e326b0252 Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Fri, 10 May 2019 14:27:01 +0200 Subject: [PATCH 07/11] Improvements after review Signed-off-by: Kostiantyn Stepaniuk --- src/esperanza/finalizationstate.cpp | 19 +++++----------- src/esperanza/finalizationstate.h | 1 - .../esperanza/finalizationstate_utils.cpp | 4 ++++ src/test/esperanza/finalizationstate_utils.h | 1 + test/functional/finalization_vote.py | 22 ++++++++++--------- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/esperanza/finalizationstate.cpp b/src/esperanza/finalizationstate.cpp index 12ea9a81fd..7f0deb4346 100644 --- a/src/esperanza/finalizationstate.cpp +++ b/src/esperanza/finalizationstate.cpp @@ -155,8 +155,8 @@ void FinalizationState::InstaJustify() { m_last_justified_epoch = m_current_epoch - 1; if (m_current_epoch > 1) { - uint32_t prev_justified = m_current_epoch - 2; - if (GetCheckpoint(prev_justified).m_is_justified) { + uint32_t to_be_finalized = m_current_epoch - 2; + if (GetCheckpoint(to_be_finalized).m_is_justified) { cp.m_is_finalized = true; m_last_finalized_epoch = m_last_justified_epoch; } @@ -202,19 +202,14 @@ ufp64::ufp64_t FinalizationState::GetCollectiveRewardFactor() { return 0; } - // we use "m_current_epoch - 2" epoch to retrieve dynasty votes because: - // -1 as m_current_epoch is already incremented inside of InitializeEpoch - // before this function is invoked - // -1 because when we store votes we use vote.m_target_epoch which is always - // one below the m_current_epoch - const uint32_t checkpoint_epoch = m_current_epoch - 2; + assert(m_last_finalized_epoch == m_current_epoch - 2); ufp64::ufp64_t curVoteFraction = ufp64::div_2uint( - GetCheckpoint(checkpoint_epoch).GetCurDynastyVotes(m_expected_source_epoch), + GetCheckpoint(m_last_finalized_epoch).GetCurDynastyVotes(m_expected_source_epoch), m_cur_dyn_deposits); ufp64::ufp64_t prevVoteFraction = ufp64::div_2uint( - GetCheckpoint(checkpoint_epoch).GetPrevDynastyVotes(m_expected_source_epoch), + GetCheckpoint(m_last_finalized_epoch).GetPrevDynastyVotes(m_expected_source_epoch), m_prev_dyn_deposits); ufp64::ufp64_t voteFraction = ufp64::min(curVoteFraction, prevVoteFraction); @@ -259,10 +254,6 @@ uint64_t FinalizationState::GetDepositSize(const uint160 &validatorAddress) cons } } -uint32_t FinalizationState::GetExpectedSourceEpoch() const { - return m_expected_source_epoch; -} - uint32_t FinalizationState::GetRecommendedTargetEpoch() const { return m_recommended_target_epoch; } diff --git a/src/esperanza/finalizationstate.h b/src/esperanza/finalizationstate.h index 0421b9e041..d47dc061bf 100644 --- a/src/esperanza/finalizationstate.h +++ b/src/esperanza/finalizationstate.h @@ -133,7 +133,6 @@ class FinalizationState : public FinalizationStateData { uint64_t GetDepositSize(const uint160 &validatorAddress) const; - uint32_t GetExpectedSourceEpoch() const; uint32_t GetRecommendedTargetEpoch() const; Vote GetRecommendedVote(const uint160 &validatorAddress) const; diff --git a/src/test/esperanza/finalizationstate_utils.cpp b/src/test/esperanza/finalizationstate_utils.cpp index 9847a26ce0..f6e13c9eb3 100644 --- a/src/test/esperanza/finalizationstate_utils.cpp +++ b/src/test/esperanza/finalizationstate_utils.cpp @@ -85,6 +85,10 @@ void FinalizationStateSpy::shuffle() { #undef ConstRand +uint32_t FinalizationStateSpy::GetExpectedSourceEpoch() const { + return m_expected_source_epoch; +} + void FinalizationStateSpy::CreateAndActivateDeposit(const uint160 &validator_address, CAmount deposit_size) { BOOST_REQUIRE_EQUAL(GetCurrentEpoch(), 0); diff --git a/src/test/esperanza/finalizationstate_utils.h b/src/test/esperanza/finalizationstate_utils.h index f2fd5ca03f..2a3076ff24 100644 --- a/src/test/esperanza/finalizationstate_utils.h +++ b/src/test/esperanza/finalizationstate_utils.h @@ -50,6 +50,7 @@ class FinalizationStateSpy : public FinalizationState { return m_settings.bounty_fraction_denominator; } + uint32_t GetExpectedSourceEpoch() const; void CreateAndActivateDeposit(const uint160 &validator_address, CAmount deposit_size); void CreateDeposit(const uint160 &finalizer_address, CAmount deposit_size); diff --git a/test/functional/finalization_vote.py b/test/functional/finalization_vote.py index 1c529e1bba..7d72058039 100755 --- a/test/functional/finalization_vote.py +++ b/test/functional/finalization_vote.py @@ -152,16 +152,7 @@ def run_test(self): generate_block(node0, count=4) prev_tx = finalizer1.decoderawtransaction(prev_tx)['txid'] - # test that node recognizes old and invalid votes. - # tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 1, 2, prev_tx) - # assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) - # tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 2, 3, prev_tx) - # assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) - # tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 7, 6, prev_tx) - # assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) - self.log.info('Tested outdated and invalid vote votes') - - # check that make_vote_tx works as expected (we really rely on this guy on tests above) + # check that make_vote_tx works as expected tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 5, 6, prev_tx) node0.sendrawtransaction(tx) self.wait_for_vote_and_disconnect(finalizer=finalizer2, node=node0) @@ -175,6 +166,17 @@ def run_test(self): 'validators': 3}) self.log.info('make_vote_tx works together with real finalizers') + # test that node recognizes old and invalid votes. + tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 1, 2, prev_tx) + assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) + tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 2, 3, prev_tx) + assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) + tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 7, 9, prev_tx) + assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) + tx = make_vote_tx(finalizer1, address1, node0.getblockhash(30), 7, 6, prev_tx) + assert_raises_rpc_error(-26, 'bad-vote-invalid', node0.sendrawtransaction, tx) + self.log.info('Tested outdated and invalid vote votes') + # UNIT-E TODO: there is a know issue https://github.com/dtr-org/unit-e/issues/643 # that finalizer doesn't vote after processing the checkpoint. # Once it's resolved, the bellow test must be uncommented From b496602692512396e78cc67b5558b9797251ec16 Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Mon, 13 May 2019 16:00:27 +0200 Subject: [PATCH 08/11] Fix lint Signed-off-by: Kostiantyn Stepaniuk --- test/functional/finalization_slash_itself.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/finalization_slash_itself.py b/test/functional/finalization_slash_itself.py index 8ba5dacb89..d35d49cd4e 100755 --- a/test/functional/finalization_slash_itself.py +++ b/test/functional/finalization_slash_itself.py @@ -18,7 +18,6 @@ disconnect_nodes, generate_block, sync_blocks, - sync_mempools, wait_until, ) From 47edbaa37836d51203dff61b4365db9a8632d669 Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Mon, 13 May 2019 19:01:05 +0200 Subject: [PATCH 09/11] Act upon reviews Signed-off-by: Kostiantyn Stepaniuk --- .../finalizationstate_calculate_withdraw_amount_tests.cpp | 2 +- src/test/finalization/state_processor_tests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp b/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp index ce39c71023..17b1cfae37 100644 --- a/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp +++ b/src/test/esperanza/finalizationstate_calculate_withdraw_amount_tests.cpp @@ -4,7 +4,7 @@ #include -BOOST_FIXTURE_TEST_SUITE(finalizationstate_calculate_withdraw_amount_tests, TestingSetup) +BOOST_FIXTURE_TEST_SUITE(finalizationstate_calculate_withdraw_amount_tests, ReducedTestingSetup) void CreateAndProcessVote(FinalizationStateSpy &state, uint160 finalizer_address, uint256 target_hash) { Vote vote{finalizer_address, target_hash, state.GetExpectedSourceEpoch(), state.GetRecommendedTargetEpoch()}; diff --git a/src/test/finalization/state_processor_tests.cpp b/src/test/finalization/state_processor_tests.cpp index 02bf40fcf0..ab8ae6d512 100644 --- a/src/test/finalization/state_processor_tests.cpp +++ b/src/test/finalization/state_processor_tests.cpp @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(trimming) { BOOST_CHECK(fixture.GetState(10) != nullptr); // Generate next epoch. - // Now epoch 2 must be finalized and repository trimmed until the last justification height + // Now epoch 2 must be finalized and repository trimmed until the last finalized height fixture.AddBlocks(5); BOOST_CHECK(fixture.GetState(5) == nullptr); BOOST_CHECK(fixture.GetState(6) == nullptr); From 53932108cd12d091fb03182427e7be7b37217c6f Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Tue, 14 May 2019 15:17:59 +0200 Subject: [PATCH 10/11] Fix finalization_state_restoration.py Signed-off-by: Kostiantyn Stepaniuk --- test/functional/finalization_state_restoration.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/functional/finalization_state_restoration.py b/test/functional/finalization_state_restoration.py index ae3273839f..59261d2fd9 100755 --- a/test/functional/finalization_state_restoration.py +++ b/test/functional/finalization_state_restoration.py @@ -30,12 +30,13 @@ def setup_deposit(self, proposer, validators): deptx = n.deposit(n.new_address, 1500) self.wait_for_transaction(deptx) - generate_block(proposer, count=24) - assert_equal(proposer.getblockcount(), 25) + generate_block(proposer, count=14) + assert_equal(proposer.getblockcount(), 15) sync_blocks(validators + [proposer]) for v in validators: disconnect_nodes(proposer, v.index) + class FinalizatoinStateRestoration(UnitETestFramework): def set_test_params(self): self.num_nodes = 2 @@ -64,7 +65,7 @@ def run_test(self): setup_deposit(self, p, [v]) self.log.info("Generate few epochs") - self.generate_epoch(p, v, count=2) + self.generate_epoch(p, v, count=4) assert_equal(p.getblockcount(), 35) assert_finalizationstate(p, {'currentEpoch': 7, @@ -143,5 +144,6 @@ def run_test(self): 'lastFinalizedEpoch': 12, 'validators': 1}) + if __name__ == '__main__': FinalizatoinStateRestoration().main() From 149fb96fbf953053ec7cb83319d15596f5756ed8 Mon Sep 17 00:00:00 2001 From: Kostiantyn Stepaniuk Date: Tue, 14 May 2019 20:27:11 +0200 Subject: [PATCH 11/11] Fix feature_reindex_commits.py Signed-off-by: Kostiantyn Stepaniuk --- test/functional/feature_reindex_commits.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/functional/feature_reindex_commits.py b/test/functional/feature_reindex_commits.py index 75507b8e21..5b175450c0 100755 --- a/test/functional/feature_reindex_commits.py +++ b/test/functional/feature_reindex_commits.py @@ -62,13 +62,13 @@ def run_test(self): disconnect_nodes(self.proposer, self.finalizer.index) - self.log.info("Generate 2 epochs") - assert_equal(self.proposer.getblockcount(), 25) + self.log.info("Generate 4 epochs") + assert_equal(self.proposer.getblockcount(), 15) votes = self.generate_epoch( proposer=self.proposer, finalizer=self.finalizer, - count=2) - assert_equal(len(votes), 2) + count=4) + assert_equal(len(votes), 4) assert_equal(self.proposer.getblockcount(), 35) assert_finalizationstate(self.proposer, {'currentEpoch': 7, @@ -116,8 +116,8 @@ def setup_deposit(self): "", "legacy"), MIN_DEPOSIT) self.wait_for_transaction(deposit_tx) - generate_block(self.proposer, count=24) - assert_equal(self.proposer.getblockcount(), 25) + generate_block(self.proposer, count=14) + assert_equal(self.proposer.getblockcount(), 15) def assert_finalizer_status(self, status): fn = self.finalizer.getvalidatorinfo