diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -173,19 +173,48 @@ sessionKey.MakeNewKey(true); } + // If avalanche is enabled and a proof is supplied, make sure it does + // not contain garbage. At this point the validity of the utxos cannot + // be checked, so only basic verification is performed. if (gArgs.IsArgSet("-avaproof")) { peerData = std::make_unique(); - { - // The proof. - CDataStream stream(ParseHex(gArgs.GetArg("-avaproof", "")), - SER_NETWORK, 0); - stream >> peerData->proof; - - // Schedule proof registration at the first new block after IBD. - mustRegisterProof = true; + // The proof. + CDataStream stream(ParseHex(gArgs.GetArg("-avaproof", "")), SER_NETWORK, + 0); + stream >> peerData->proof; + + ProofValidationState proof_state; + if (!peerData->proof.verify(proof_state)) { + LogPrintfToBeContinued("Failed basic avaproof verification: "); + switch (proof_state.GetResult()) { + case avalanche::ProofValidationResult::NO_STAKE: + LogPrintf("the avalanche proof has no stake\n"); + break; + case avalanche::ProofValidationResult::DUST_THRESOLD: + LogPrintf("the avalanche proof stake is too low\n"); + break; + case avalanche::ProofValidationResult::DUPLICATE_STAKE: + LogPrintf("the avalanche proof has duplicated stake\n"); + break; + case avalanche::ProofValidationResult::INVALID_SIGNATURE: + LogPrintf("the avalanche proof has invalid stake " + "signatures\n"); + break; + case avalanche::ProofValidationResult::TOO_MANY_UTXOS: + LogPrintf("the avalanche proof has too many utxos " + "(max: %u)\n", + AVALANCHE_MAX_PROOF_STAKES); + break; + default: + LogPrintf("the avalanche proof is invalid\n"); + } + throw std::runtime_error("Failed basic avaproof verification"); } + // Schedule proof registration at first new block after IBD. + mustRegisterProof = true; + // Generate the delegation to the session key. DelegationBuilder dgb(peerData->proof); if (sessionKey.GetPubKey() != peerData->proof.getMaster()) { @@ -193,6 +222,10 @@ sessionKey.GetPubKey()); } peerData->delegation = dgb.build(); + } else if (gArgs.GetBoolArg("-enableavalanche", + AVALANCHE_DEFAULT_ENABLED)) { + LogPrintf("Avalanche is enabled but no proof supplied, the node " + "will not be able to vote\n"); } // Make sure we get notified of chain state changes. diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -2432,48 +2432,15 @@ } // Step 6.5 (I guess ?): Initialize Avalanche. - g_avalanche = std::make_unique( - *node.chain, node.connman.get(), node.peerman.get()); + try { + g_avalanche = std::make_unique( + *node.chain, node.connman.get(), node.peerman.get()); + } catch (const std::runtime_error &e) { + InitError(_("Failed to initialize avalanche")); + return false; + } if (args.GetBoolArg("-enableavalanche", AVALANCHE_DEFAULT_ENABLED)) { nLocalServices = ServiceFlags(nLocalServices | NODE_AVALANCHE); - - // If avalanche is enabled and a proof is supplied, make sure it does - // not contain garbage. At this point the validity of the utxos cannot - // be checked, so only basic verification is performed. - const avalanche::Proof *proof = g_avalanche->getLocalProof(); - - if (proof) { - avalanche::ProofValidationState proof_state; - if (!proof->verify(proof_state)) { - switch (proof_state.GetResult()) { - case avalanche::ProofValidationResult::NO_STAKE: - InitError(_("the avalanche proof has no stake")); - return false; - case avalanche::ProofValidationResult::DUST_THRESOLD: - InitError(_("the avalanche proof stake is too low")); - return false; - case avalanche::ProofValidationResult::DUPLICATE_STAKE: - InitError( - _("the avalanche proof has duplicated stake")); - return false; - case avalanche::ProofValidationResult::INVALID_SIGNATURE: - InitError(_("the avalanche proof has invalid stake " - "signatures")); - return false; - case avalanche::ProofValidationResult::TOO_MANY_UTXOS: - InitError(strprintf(_("the avalanche proof has too " - "many utxos (max: %u)"), - AVALANCHE_MAX_PROOF_STAKES)); - return false; - default: - InitError(_("the avalanche proof is invalid")); - return false; - } - } - } else { - LogPrintf("Avalanche is enabled but no proof supplied, the node " - "will not be able to vote\n"); - } } // Step 7: load block chain diff --git a/test/functional/abc_rpc_avalancheproof.py b/test/functional/abc_rpc_avalancheproof.py --- a/test/functional/abc_rpc_avalancheproof.py +++ b/test/functional/abc_rpc_avalancheproof.py @@ -187,14 +187,18 @@ self.stop_node(0) + init_error_msg = "Error: Failed to initialize avalanche" + debug_msg_head = "Failed basic avaproof verification: " + def check_proof_init_error(proof, message): - node.assert_start_raises_init_error( - self.extra_args[0] + [ - "-avaproof={}".format(proof), - "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", - ], - expected_msg="Error: " + message, - ) + with node.assert_debug_log([debug_msg_head + message]): + node.assert_start_raises_init_error( + self.extra_args[0] + [ + "-avaproof={}".format(proof), + "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", + ], + expected_msg=init_error_msg, + ) check_proof_init_error(no_stake, "the avalanche proof has no stake") @@ -207,13 +211,15 @@ # The too many utxos case creates a proof which is that large that it # cannot fit on the command line append_config(node.datadir, ["avaproof={}".format(too_many_utxos)]) - node.assert_start_raises_init_error( - self.extra_args[0] + [ - "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", - ], - expected_msg="Error: the avalanche proof has too many utxos", - match=ErrorMatch.PARTIAL_REGEX, - ) + with node.assert_debug_log( + [debug_msg_head + "the avalanche proof has too many utxos"]): + node.assert_start_raises_init_error( + self.extra_args[0] + [ + "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", + ], + expected_msg=init_error_msg, + match=ErrorMatch.PARTIAL_REGEX, + ) if __name__ == '__main__':