diff --git a/src/avalanche/avalanche.h b/src/avalanche/avalanche.h --- a/src/avalanche/avalanche.h +++ b/src/avalanche/avalanche.h @@ -30,6 +30,12 @@ */ static constexpr size_t AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN = 60; +/** + * Peer replacement cooldown time default value in seconds. + * Minimal delay before a peer can be replaced due to a conflicting proof. + */ +static constexpr size_t AVALANCHE_DEFAULT_PEER_REPLACEMENT_COOLDOWN = 60 * 60; + /** * Is proof replacement enabled by default. */ diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1284,6 +1284,11 @@ "in seconds (default: %u)", AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN), ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE); + argsman.AddArg("-avalanchepeerreplacementcooldown", + strprintf("Mandatory cooldown before a peer can be replaced " + "in seconds (default: %u)", + AVALANCHE_DEFAULT_PEER_REPLACEMENT_COOLDOWN), + ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE); argsman.AddArg("-enableavalancheproofreplacement", strprintf("Enable avalanche proof replacement (default: %u)", AVALANCHE_DEFAULT_PROOF_REPLACEMENT_ENABLED), diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5018,6 +5018,7 @@ const avalanche::ProofId &proofid = proof->getId(); auto rejectionMode = avalanche::PeerManager::RejectionMode::DEFAULT; + auto nextCooldownTimePoint = GetTime(); switch (u.getStatus()) { case avalanche::VoteStatus::Invalid: rejectionMode = @@ -5038,8 +5039,11 @@ proofid.GetHex()); } break; - case avalanche::VoteStatus::Accepted: case avalanche::VoteStatus::Finalized: + nextCooldownTimePoint += std::chrono::seconds(gArgs.GetArg( + "-avalanchepeerreplacementcooldown", + AVALANCHE_DEFAULT_PEER_REPLACEMENT_COOLDOWN)); + case avalanche::VoteStatus::Accepted: LogPrint(BCLog::AVALANCHE, "Avalanche accepted proof %s, status %d\n", proofid.GetHex(), uint8_t(u.getStatus())); @@ -5048,7 +5052,14 @@ pm.registerProof( proof, avalanche::PeerManager:: RegistrationMode::FORCE_ACCEPT); - return pm.isBoundToPeer(proofid); + return pm.forPeer( + proofid, [&](const avalanche::Peer &peer) { + pm.updateNextPossibleConflictTime( + peer.peerid, nextCooldownTimePoint); + // Only fail if the peer was not + // created + return true; + }); })) { LogPrint(BCLog::AVALANCHE, "ERROR: Failed to accept proof: %s\n", diff --git a/test/functional/abc_p2p_avalanche_proof_voting.py b/test/functional/abc_p2p_avalanche_proof_voting.py --- a/test/functional/abc_p2p_avalanche_proof_voting.py +++ b/test/functional/abc_p2p_avalanche_proof_voting.py @@ -29,9 +29,10 @@ self.setup_clean_chain = True self.num_nodes = 1 self.conflicting_proof_cooldown = 100 + self.peer_replacement_cooldown = 2000 self.extra_args = [ ['-enableavalanche=1', '-enableavalancheproofreplacement=1', - f'-avalancheconflictingproofcooldown={self.conflicting_proof_cooldown}', '-avacooldown=0'], + f'-avalancheconflictingproofcooldown={self.conflicting_proof_cooldown}', f'-avalanchepeerreplacementcooldown={self.peer_replacement_cooldown}', '-avacooldown=0'], ] self.supports_cli = False @@ -216,12 +217,28 @@ self.wait_until(lambda: accept_proof(proofid_seq30)) assert proofid_seq40 not in get_proof_ids(node) - self.log.info("Test proof rejection") + self.log.info("Test the peer replacement rate limit") + + # Wait until proof_seq30 is finalized + with node.assert_debug_log([f"Avalanche accepted proof {proofid_seq30:0{64}x}, status 3"]): + self.wait_until(lambda: not can_find_proof_in_poll( + proofid_seq30, response=AvalancheVoteError.ACCEPTED)) + # Not enough + assert self.conflicting_proof_cooldown < self.peer_replacement_cooldown mock_time += self.conflicting_proof_cooldown node.setmocktime(mock_time) peer = get_ava_p2p_interface(node) + + with node.assert_debug_log(["Not polling the avalanche proof (cooldown-not-elapsed)"]): + send_proof(peer, proof_seq50) + + mock_time += self.peer_replacement_cooldown + node.setmocktime(mock_time) + + self.log.info("Test proof rejection") + send_proof(peer, proof_seq50) self.wait_until(lambda: proofid_seq50 in get_proof_ids(node)) assert proofid_seq40 not in get_proof_ids(node)