diff --git a/src/avalanche/delegation.h b/src/avalanche/delegation.h --- a/src/avalanche/delegation.h +++ b/src/avalanche/delegation.h @@ -43,6 +43,7 @@ const DelegationId &getId() const { return dgid; } const ProofId &getProofId() const { return proofid; } + std::optional getMaster() const; SERIALIZE_METHODS(Delegation, obj) { READWRITE(obj.proofid, obj.levels); diff --git a/src/avalanche/delegation.cpp b/src/avalanche/delegation.cpp --- a/src/avalanche/delegation.cpp +++ b/src/avalanche/delegation.cpp @@ -60,4 +60,10 @@ return ret; } +std::optional Delegation::getMaster() const { + return levels.size() == 0 + ? std::nullopt + : std::make_optional(levels.back().pubkey); +} + } // namespace avalanche diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -251,9 +251,7 @@ // Generate the delegation to the session key. DelegationBuilder dgb(peerData->proof); - if (sessionKey.GetPubKey() != peerData->proof.getMaster()) { - dgb.addLevel(masterKey, sessionKey.GetPubKey()); - } + dgb.addLevel(masterKey, sessionKey.GetPubKey()); peerData->delegation = dgb.build(); } diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -4025,16 +4025,25 @@ avalanche::Delegation &delegation = pfrom.m_avalanche_state->delegation; verifier >> delegation; - avalanche::Proof proof; - avalanche::DelegationState state; - CPubKey pubkey; - if (!delegation.verify(state, proof, pubkey)) { - Misbehaving(pfrom, 100, "invalid-delegation"); + std::optional master = delegation.getMaster(); + if (!master) { + LogPrint(BCLog::NET, + "Ignoring AVAHELLO from peer=%d with empty delegation\n", + pfrom.GetId()); return; } SchnorrSig sig; verifier >> sig; + if (!master->VerifySchnorr(g_avalanche->buildRemoteSighash(&pfrom), + sig)) { + Misbehaving(pfrom, 100, "invalid-avahello-signature"); + return; + } + LogPrint(BCLog::NET, + "Successfully checked AVAHELLO signature for peer=%d\n", + pfrom.GetId()); + return; } if (msg_type == NetMsgType::AVAPOLL) { diff --git a/test/functional/abc_p2p_avalanche.py b/test/functional/abc_p2p_avalanche.py --- a/test/functional/abc_p2p_avalanche.py +++ b/test/functional/abc_p2p_avalanche.py @@ -4,17 +4,24 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the resolution of forks via avalanche.""" import random +import struct from test_framework.avatools import create_coinbase_stakes from test_framework.key import ( + bytes_to_wif, ECKey, ECPubKey, ) from test_framework.mininode import P2PInterface, mininode_lock from test_framework.messages import ( + AvalancheDelegation, + AvalancheProof, AvalancheResponse, AvalancheVote, CInv, + FromHex, + hash256, + msg_avahello, msg_avapoll, msg_tcpavaresponse, NODE_AVALANCHE, @@ -110,6 +117,35 @@ with mininode_lock: return self.avahello + def _getLocalSigHash(self, delegation_id: bytes): + b = delegation_id + b += struct.pack(" P2PInterface)") + interface = get_node() + avahello = interface.wait_for_avahello().hello avakey.set(bytes.fromhex(node.getavalanchekey())) assert avakey.verify_schnorr( - avahello.sig, avahello.get_sighash(poll_node)) + avahello.sig, avahello.get_sighash(interface)) + + self.log.info("Test the avahello signature (P2PInterface -> node)") + stakes = create_coinbase_stakes(node, [blockhashes[1]], addrkey0.key) + interface_proof_hex = node.buildavalancheproof( + proof_sequence, proof_expiration, pubkey.get_bytes().hex(), + stakes) + # delegate + delegated_privkey = ECKey() + delegated_privkey.generate() + interface_delegation_hex = node.delegateavalancheproof( + interface_proof_hex, + bytes_to_wif(privkey.get_bytes()), + delegated_privkey.get_pubkey().get_bytes().hex(), + None + ) + + with self.nodes[0].assert_debug_log(["Successfully checked AVAHELLO signature for peer=0"]): + interface.send_avahello( + interface_delegation_hex, + delegated_privkey) + + self.log.info("Test a wrong avahello signature") + interface = get_node() + with self.nodes[0].assert_debug_log( + ["Misbehaving", "peer=1 (0 -> 100) BAN THRESHOLD EXCEEDED: invalid-avahello-signature"]): + interface.send_avahello_wrong_signature(interface_delegation_hex) + + self.log.info("Test an avahello with an empty delegation") + interface = get_node() + proofid = FromHex(AvalancheProof(), interface_proof_hex).proofid + with self.nodes[0].assert_debug_log( + ["Ignoring AVAHELLO from peer=2 with empty delegation"]): + interface.send_avahello_empty_delegation( + proofid, delegated_privkey) if __name__ == '__main__':