Bitcoin ABC 0.30.5
P2P Digital Currency
avalanche.cpp
Go to the documentation of this file.
1// Copyright (c) 2020 The Bitcoin developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
10#include <avalanche/proof.h>
13#include <common/args.h>
14#include <config.h>
15#include <core_io.h>
16#include <index/txindex.h>
17#include <key_io.h>
18#include <net_processing.h>
19#include <node/context.h>
21#include <rpc/blockchain.h>
22#include <rpc/server.h>
23#include <rpc/server_util.h>
24#include <rpc/util.h>
25#include <util/strencodings.h>
26#include <util/translation.h>
27
28#include <univalue.h>
29
32
34 return RPCHelpMan{
35 "getavalanchekey",
36 "Returns the key used to sign avalanche messages.\n",
37 {},
39 RPCExamples{HelpExampleRpc("getavalanchekey", "")},
40 [&](const RPCHelpMan &self, const Config &config,
41 const JSONRPCRequest &request) -> UniValue {
42 NodeContext &node = EnsureAnyNodeContext(request.context);
44 return HexStr(avalanche.getSessionPubKey());
45 },
46 };
47}
48
49static CPubKey ParsePubKey(const UniValue &param) {
50 const std::string keyHex = param.get_str();
51 if ((keyHex.length() != 2 * CPubKey::COMPRESSED_SIZE &&
52 keyHex.length() != 2 * CPubKey::SIZE) ||
53 !IsHex(keyHex)) {
55 strprintf("Invalid public key: %s\n", keyHex));
56 }
57
58 return HexToPubKey(keyHex);
59}
60
64 auto localProof = avalanche.getLocalProof();
65 if (localProof && localProof->getId() == proof->getId()) {
66 return true;
67 }
68
69 return avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
70 return pm.getProof(proof->getId()) || pm.registerProof(proof, state);
71 });
72}
73
75 avalanche::ProofRef proof) {
77 return registerProofIfNeeded(avalanche, std::move(proof), state);
78}
79
81 const std::string &dgHex, CPubKey &auth) {
83 if (!avalanche::Delegation::FromHex(dg, dgHex, error)) {
85 }
86
88 if (!dg.verify(state, auth)) {
90 "The delegation is invalid: " + state.ToString());
91 }
92}
93
95 const std::string &proofHex) {
97 if (!avalanche::Proof::FromHex(proof, proofHex, error)) {
99 }
100
101 Amount stakeUtxoDustThreshold = avalanche::PROOF_DUST_THRESHOLD;
102 if (node.avalanche) {
103 // If Avalanche is enabled, use the configured dust threshold
104 node.avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
105 stakeUtxoDustThreshold = pm.getStakeUtxoDustThreshold();
106 });
107 }
108
110 {
111 LOCK(cs_main);
112 if (!proof.verify(stakeUtxoDustThreshold, *Assert(node.chainman),
113 state)) {
115 "The proof is invalid: " + state.ToString());
116 }
117 }
118}
119
121 return RPCHelpMan{
122 "addavalanchenode",
123 "Add a node in the set of peers to poll for avalanche.\n",
124 {
126 "Node to be added to avalanche."},
128 "The public key of the node."},
130 "Proof that the node is not a sybil."},
132 "The proof delegation the the node public key"},
133 },
135 "Whether the addition succeeded or not."},
137 HelpExampleRpc("addavalanchenode", "5, \"<pubkey>\", \"<proof>\"")},
138 [&](const RPCHelpMan &self, const Config &config,
139 const JSONRPCRequest &request) -> UniValue {
140 const NodeId nodeid = request.params[0].getInt<int64_t>();
141 CPubKey key = ParsePubKey(request.params[1]);
142
143 auto proof = RCUPtr<avalanche::Proof>::make();
144 NodeContext &node = EnsureAnyNodeContext(request.context);
146
147 verifyProofOrThrow(node, *proof, request.params[2].get_str());
148
149 const avalanche::ProofId &proofid = proof->getId();
150 if (key != proof->getMaster()) {
151 if (request.params.size() < 4 || request.params[3].isNull()) {
152 throw JSONRPCError(
154 "The public key does not match the proof");
155 }
156
158 CPubKey auth;
159 verifyDelegationOrThrow(dg, request.params[3].get_str(), auth);
160
161 if (dg.getProofId() != proofid) {
162 throw JSONRPCError(
164 "The delegation does not match the proof");
165 }
166
167 if (key != auth) {
168 throw JSONRPCError(
170 "The public key does not match the delegation");
171 }
172 }
173
174 if (!registerProofIfNeeded(avalanche, proof)) {
176 "The proof has conflicting utxos");
177 }
178
179 if (!node.connman->ForNode(nodeid, [&](CNode *pnode) {
180 LOCK(pnode->cs_avalanche_pubkey);
181 bool expected = false;
182 if (pnode->m_avalanche_enabled.compare_exchange_strong(
183 expected, true)) {
184 pnode->m_avalanche_pubkey = std::move(key);
185 }
186 return true;
187 })) {
188 throw JSONRPCError(
190 strprintf("The node does not exist: %d", nodeid));
191 }
192
193 return avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
194 if (!pm.addNode(nodeid, proofid)) {
195 return false;
196 }
197
198 pm.addUnbroadcastProof(proofid);
199 return true;
200 });
201 },
202 };
203}
204
206 return RPCHelpMan{
207 "buildavalancheproof",
208 "Build a proof for avalanche's sybil resistance.\n",
209 {
211 "The proof's sequence"},
213 "A timestamp indicating when the proof expire"},
215 "The master private key in base58-encoding"},
216 {
217 "stakes",
220 "The stakes to be signed and associated private keys",
221 {
222 {
223 "stake",
226 "A stake to be attached to this proof",
227 {
228 {"txid", RPCArg::Type::STR_HEX,
229 RPCArg::Optional::NO, "The transaction id"},
231 "The output number"},
232 {"amount", RPCArg::Type::AMOUNT,
233 RPCArg::Optional::NO, "The amount in this UTXO"},
235 "The height at which this UTXO was mined"},
236 {"iscoinbase", RPCArg::Type::BOOL,
237 RPCArg::Default{false},
238 "Indicate wether the UTXO is a coinbase"},
239 {"privatekey", RPCArg::Type::STR,
241 "private key in base58-encoding"},
242 },
243 },
244 },
245 },
246 {"payoutAddress", RPCArg::Type::STR, RPCArg::Optional::NO,
247 "A payout address"},
248 },
250 "A string that is a serialized, hex-encoded proof data."},
251 RPCExamples{HelpExampleRpc("buildavalancheproof",
252 "0 1234567800 \"<master>\" []")},
253 [&](const RPCHelpMan &self, const Config &config,
254 const JSONRPCRequest &request) -> UniValue {
255 const uint64_t sequence = request.params[0].getInt<int64_t>();
256 const int64_t expiration = request.params[1].getInt<int64_t>();
257
258 CKey masterKey = DecodeSecret(request.params[2].get_str());
259 if (!masterKey.IsValid()) {
260 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid master key");
261 }
262
263 CTxDestination payoutAddress = DecodeDestination(
264 request.params[4].get_str(), config.GetChainParams());
265
266 if (!IsValidDestination(payoutAddress)) {
268 "Invalid payout address");
269 }
270
271 avalanche::ProofBuilder pb(sequence, expiration, masterKey,
272 GetScriptForDestination(payoutAddress));
273
274 const UniValue &stakes = request.params[3].get_array();
275 for (size_t i = 0; i < stakes.size(); i++) {
276 const UniValue &stake = stakes[i];
278 stake,
279 {
280 {"txid", UniValue::VSTR},
281 {"vout", UniValue::VNUM},
282 // "amount" is also required but check is done below
283 // due to UniValue::VNUM erroneously not accepting
284 // quoted numerics (which are valid JSON)
285 {"height", UniValue::VNUM},
286 {"privatekey", UniValue::VSTR},
287 });
288
289 int nOut = stake.find_value("vout").getInt<int>();
290 if (nOut < 0) {
292 "vout cannot be negative");
293 }
294
295 const int height = stake.find_value("height").getInt<int>();
296 if (height < 1) {
298 "height must be positive");
299 }
300
301 const TxId txid(ParseHashO(stake, "txid"));
302 const COutPoint utxo(txid, nOut);
303
304 if (!stake.exists("amount")) {
305 throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount");
306 }
307
308 const Amount amount =
309 AmountFromValue(stake.find_value("amount"));
310
311 const UniValue &iscbparam = stake.find_value("iscoinbase");
312 const bool iscoinbase =
313 iscbparam.isNull() ? false : iscbparam.get_bool();
314 CKey key =
315 DecodeSecret(stake.find_value("privatekey").get_str());
316
317 if (!key.IsValid()) {
319 "Invalid private key");
320 }
321
322 if (!pb.addUTXO(utxo, amount, uint32_t(height), iscoinbase,
323 std::move(key))) {
325 "Duplicated stake");
326 }
327 }
328
329 const avalanche::ProofRef proof = pb.build();
330
331 return proof->ToHex();
332 },
333 };
334}
335
337 return RPCHelpMan{
338 "decodeavalancheproof",
339 "Convert a serialized, hex-encoded proof, into JSON object. "
340 "The validity of the proof is not verified.\n",
341 {
343 "The proof hex string"},
344 },
345 RPCResult{
347 "",
348 "",
349 {
350 {RPCResult::Type::NUM, "sequence",
351 "The proof's sequential number"},
352 {RPCResult::Type::NUM, "expiration",
353 "A timestamp indicating when the proof expires"},
354 {RPCResult::Type::STR_HEX, "master", "The master public key"},
355 {RPCResult::Type::STR, "signature",
356 "The proof signature (base64 encoded)"},
358 "payoutscript",
359 "The proof payout script",
360 {
361 {RPCResult::Type::STR, "asm", "Decoded payout script"},
363 "Raw payout script in hex format"},
364 {RPCResult::Type::STR, "type",
365 "The output type (e.g. " + GetAllOutputTypes() + ")"},
366 {RPCResult::Type::NUM, "reqSigs",
367 "The required signatures"},
369 "addresses",
370 "",
371 {
372 {RPCResult::Type::STR, "address", "eCash address"},
373 }},
374 }},
375 {RPCResult::Type::STR_HEX, "limitedid",
376 "A hash of the proof data excluding the master key."},
377 {RPCResult::Type::STR_HEX, "proofid",
378 "A hash of the limitedid and master key."},
379 {RPCResult::Type::STR_AMOUNT, "staked_amount",
380 "The total staked amount of this proof in " +
381 Currency::get().ticker + "."},
382 {RPCResult::Type::NUM, "score", "The score of this proof."},
384 "stakes",
385 "",
386 {
388 "",
389 "",
390 {
392 "The transaction id"},
393 {RPCResult::Type::NUM, "vout", "The output number"},
395 "The amount in this UTXO"},
396 {RPCResult::Type::NUM, "height",
397 "The height at which this UTXO was mined"},
398 {RPCResult::Type::BOOL, "iscoinbase",
399 "Indicate whether the UTXO is a coinbase"},
400 {RPCResult::Type::STR_HEX, "pubkey",
401 "This UTXO's public key"},
402 {RPCResult::Type::STR, "signature",
403 "Signature of the proofid with this UTXO's private "
404 "key (base64 encoded)"},
405 }},
406 }},
407 }},
408 RPCExamples{HelpExampleCli("decodeavalancheproof", "\"<hex proof>\"") +
409 HelpExampleRpc("decodeavalancheproof", "\"<hex proof>\"")},
410 [&](const RPCHelpMan &self, const Config &config,
411 const JSONRPCRequest &request) -> UniValue {
412 avalanche::Proof proof;
414 if (!avalanche::Proof::FromHex(proof, request.params[0].get_str(),
415 error)) {
417 }
418
419 UniValue result(UniValue::VOBJ);
420 result.pushKV("sequence", proof.getSequence());
421 result.pushKV("expiration", proof.getExpirationTime());
422 result.pushKV("master", HexStr(proof.getMaster()));
423 result.pushKV("signature", EncodeBase64(proof.getSignature()));
424
425 const auto payoutScript = proof.getPayoutScript();
426 UniValue payoutScriptObj(UniValue::VOBJ);
427 ScriptPubKeyToUniv(payoutScript, payoutScriptObj,
428 /* fIncludeHex */ true);
429 result.pushKV("payoutscript", payoutScriptObj);
430
431 result.pushKV("limitedid", proof.getLimitedId().ToString());
432 result.pushKV("proofid", proof.getId().ToString());
433
434 result.pushKV("staked_amount", proof.getStakedAmount());
435 result.pushKV("score", uint64_t(proof.getScore()));
436
437 UniValue stakes(UniValue::VARR);
438 for (const avalanche::SignedStake &s : proof.getStakes()) {
439 const COutPoint &utxo = s.getStake().getUTXO();
441 stake.pushKV("txid", utxo.GetTxId().ToString());
442 stake.pushKV("vout", uint64_t(utxo.GetN()));
443 stake.pushKV("amount", s.getStake().getAmount());
444 stake.pushKV("height", uint64_t(s.getStake().getHeight()));
445 stake.pushKV("iscoinbase", s.getStake().isCoinbase());
446 stake.pushKV("pubkey", HexStr(s.getStake().getPubkey()));
447 // Only PKHash destination is supported, so this is safe
448 stake.pushKV("address",
449 EncodeDestination(PKHash(s.getStake().getPubkey()),
450 config));
451 stake.pushKV("signature", EncodeBase64(s.getSignature()));
452 stakes.push_back(stake);
453 }
454 result.pushKV("stakes", stakes);
455
456 return result;
457 },
458 };
459}
460
462 return RPCHelpMan{
463 "delegateavalancheproof",
464 "Delegate the avalanche proof to another public key.\n",
465 {
467 "The limited id of the proof to be delegated."},
469 "The private key in base58-encoding. Must match the proof master "
470 "public key or the upper level parent delegation public key if "
471 " supplied."},
473 "The public key to delegate the proof to."},
475 "A string that is the serialized, hex-encoded delegation for the "
476 "proof and which is a parent for the delegation to build."},
477 },
479 "A string that is a serialized, hex-encoded delegation."},
481 HelpExampleRpc("delegateavalancheproof",
482 "\"<limitedproofid>\" \"<privkey>\" \"<pubkey>\"")},
483 [&](const RPCHelpMan &self, const Config &config,
484 const JSONRPCRequest &request) -> UniValue {
485 avalanche::LimitedProofId limitedProofId{
486 ParseHashV(request.params[0], "limitedproofid")};
487
488 const CKey privkey = DecodeSecret(request.params[1].get_str());
489 if (!privkey.IsValid()) {
491 "The private key is invalid");
492 }
493
494 const CPubKey pubkey = ParsePubKey(request.params[2]);
495
496 std::unique_ptr<avalanche::DelegationBuilder> dgb;
497 if (request.params.size() >= 4 && !request.params[3].isNull()) {
499 CPubKey auth;
500 verifyDelegationOrThrow(dg, request.params[3].get_str(), auth);
501
502 if (dg.getProofId() !=
503 limitedProofId.computeProofId(dg.getProofMaster())) {
504 throw JSONRPCError(
506 "The delegation does not match the proof");
507 }
508
509 if (privkey.GetPubKey() != auth) {
510 throw JSONRPCError(
512 "The private key does not match the delegation");
513 }
514
515 dgb = std::make_unique<avalanche::DelegationBuilder>(dg);
516 } else {
517 dgb = std::make_unique<avalanche::DelegationBuilder>(
518 limitedProofId, privkey.GetPubKey());
519 }
520
521 if (!dgb->addLevel(privkey, pubkey)) {
523 "Unable to build the delegation");
524 }
525
527 ss << dgb->build();
528 return HexStr(ss);
529 },
530 };
531}
532
534 return RPCHelpMan{
535 "decodeavalanchedelegation",
536 "Convert a serialized, hex-encoded avalanche proof delegation, into "
537 "JSON object. \n"
538 "The validity of the delegation is not verified.\n",
539 {
541 "The delegation hex string"},
542 },
543 RPCResult{
545 "",
546 "",
547 {
548 {RPCResult::Type::STR_HEX, "pubkey",
549 "The public key the proof is delegated to."},
550 {RPCResult::Type::STR_HEX, "proofmaster",
551 "The delegated proof master public key."},
552 {RPCResult::Type::STR_HEX, "delegationid",
553 "The identifier of this delegation."},
554 {RPCResult::Type::STR_HEX, "limitedid",
555 "A delegated proof data hash excluding the master key."},
556 {RPCResult::Type::STR_HEX, "proofid",
557 "A hash of the delegated proof limitedid and master key."},
558 {RPCResult::Type::NUM, "depth",
559 "The number of delegation levels."},
561 "levels",
562 "",
563 {
565 "",
566 "",
567 {
568 {RPCResult::Type::NUM, "index",
569 "The index of this delegation level."},
570 {RPCResult::Type::STR_HEX, "pubkey",
571 "This delegated public key for this level"},
572 {RPCResult::Type::STR, "signature",
573 "Signature of this delegation level (base64 "
574 "encoded)"},
575 }},
576 }},
577 }},
578 RPCExamples{HelpExampleCli("decodeavalanchedelegation",
579 "\"<hex delegation>\"") +
580 HelpExampleRpc("decodeavalanchedelegation",
581 "\"<hex delegation>\"")},
582 [&](const RPCHelpMan &self, const Config &config,
583 const JSONRPCRequest &request) -> UniValue {
584 avalanche::Delegation delegation;
587 delegation, request.params[0].get_str(), error)) {
589 }
590
591 UniValue result(UniValue::VOBJ);
592 result.pushKV("pubkey", HexStr(delegation.getDelegatedPubkey()));
593 result.pushKV("proofmaster", HexStr(delegation.getProofMaster()));
594 result.pushKV("delegationid", delegation.getId().ToString());
595 result.pushKV("limitedid",
596 delegation.getLimitedProofId().ToString());
597 result.pushKV("proofid", delegation.getProofId().ToString());
598
599 auto levels = delegation.getLevels();
600 result.pushKV("depth", uint64_t(levels.size()));
601
602 UniValue levelsArray(UniValue::VARR);
603 for (auto &level : levels) {
605 obj.pushKV("pubkey", HexStr(level.pubkey));
606 obj.pushKV("signature", EncodeBase64(level.sig));
607 levelsArray.push_back(std::move(obj));
608 }
609 result.pushKV("levels", levelsArray);
610
611 return result;
612 },
613 };
614}
615
617 return RPCHelpMan{
618 "getavalancheinfo",
619 "Returns an object containing various state info regarding avalanche "
620 "networking.\n",
621 {},
622 RPCResult{
624 "",
625 "",
626 {
627 {RPCResult::Type::BOOL, "ready_to_poll",
628 "Whether the node is ready to start polling and voting."},
630 "local",
631 "Only available if -avaproof has been supplied to the node",
632 {
633 {RPCResult::Type::BOOL, "verified",
634 "Whether the node local proof has been locally verified "
635 "or not."},
636 {RPCResult::Type::STR, "verification_status",
637 "The proof verification status. Only available if the "
638 "\"verified\" flag is false."},
639 {RPCResult::Type::STR_HEX, "proofid",
640 "The node local proof id."},
641 {RPCResult::Type::STR_HEX, "limited_proofid",
642 "The node local limited proof id."},
643 {RPCResult::Type::STR_HEX, "master",
644 "The node local proof master public key."},
645 {RPCResult::Type::STR, "payout_address",
646 "The node local proof payout address. This might be "
647 "omitted if the payout script is not one of P2PK, P2PKH "
648 "or P2SH, in which case decodeavalancheproof can be used "
649 "to get more details."},
650 {RPCResult::Type::STR_AMOUNT, "stake_amount",
651 "The node local proof staked amount."},
652 }},
654 "network",
655 "",
656 {
657 {RPCResult::Type::NUM, "proof_count",
658 "The number of valid avalanche proofs we know exist "
659 "(including this node's local proof if applicable)."},
660 {RPCResult::Type::NUM, "connected_proof_count",
661 "The number of avalanche proofs with at least one node "
662 "we are connected to (including this node's local proof "
663 "if applicable)."},
664 {RPCResult::Type::NUM, "dangling_proof_count",
665 "The number of avalanche proofs with no node attached."},
666 {RPCResult::Type::NUM, "finalized_proof_count",
667 "The number of known avalanche proofs that have been "
668 "finalized by avalanche."},
669 {RPCResult::Type::NUM, "conflicting_proof_count",
670 "The number of known avalanche proofs that conflict with "
671 "valid proofs."},
672 {RPCResult::Type::NUM, "immature_proof_count",
673 "The number of known avalanche proofs that have immature "
674 "utxos."},
675 {RPCResult::Type::STR_AMOUNT, "total_stake_amount",
676 "The total staked amount over all the valid proofs in " +
678 " (including this node's local proof if "
679 "applicable)."},
680 {RPCResult::Type::STR_AMOUNT, "connected_stake_amount",
681 "The total staked amount over all the connected proofs "
682 "in " +
684 " (including this node's local proof if "
685 "applicable)."},
686 {RPCResult::Type::STR_AMOUNT, "dangling_stake_amount",
687 "The total staked amount over all the dangling proofs "
688 "in " +
690 " (including this node's local proof if "
691 "applicable)."},
692 {RPCResult::Type::STR_AMOUNT, "immature_stake_amount",
693 "The total staked amount over all the immature proofs "
694 "in " +
696 " (including this node's local proof if "
697 "applicable)."},
698 {RPCResult::Type::NUM, "node_count",
699 "The number of avalanche nodes we are connected to "
700 "(including this node if a local proof is set)."},
701 {RPCResult::Type::NUM, "connected_node_count",
702 "The number of avalanche nodes associated with an "
703 "avalanche proof (including this node if a local proof "
704 "is set)."},
705 {RPCResult::Type::NUM, "pending_node_count",
706 "The number of avalanche nodes pending for a proof."},
707 }},
708 },
709 },
710 RPCExamples{HelpExampleCli("getavalancheinfo", "") +
711 HelpExampleRpc("getavalancheinfo", "")},
712 [&](const RPCHelpMan &self, const Config &config,
713 const JSONRPCRequest &request) -> UniValue {
714 NodeContext &node = EnsureAnyNodeContext(request.context);
716
718 ret.pushKV("ready_to_poll", avalanche.isQuorumEstablished());
719
720 auto localProof = avalanche.getLocalProof();
721 if (localProof != nullptr) {
723 const bool verified = avalanche.withPeerManager(
724 [&](const avalanche::PeerManager &pm) {
725 const avalanche::ProofId &proofid = localProof->getId();
726 return pm.isBoundToPeer(proofid);
727 });
728 local.pushKV("verified", verified);
729 const bool sharing = avalanche.canShareLocalProof();
730 if (!verified) {
732 avalanche.getLocalProofRegistrationState();
733 // If the local proof is not registered but the state is
734 // valid, no registration attempt occurred yet.
735 local.pushKV("verification_status",
736 state.IsValid()
737 ? (sharing ? "pending verification"
738 : "pending inbound connections")
739 : state.GetRejectReason());
740 }
741 local.pushKV("proofid", localProof->getId().ToString());
742 local.pushKV("limited_proofid",
743 localProof->getLimitedId().ToString());
744 local.pushKV("master", HexStr(localProof->getMaster()));
745 CTxDestination destination;
746 if (ExtractDestination(localProof->getPayoutScript(),
747 destination)) {
748 local.pushKV("payout_address",
749 EncodeDestination(destination, config));
750 }
751 local.pushKV("stake_amount", localProof->getStakedAmount());
752 ret.pushKV("local", local);
753 }
754
755 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
756 UniValue network(UniValue::VOBJ);
757
758 uint64_t proofCount{0};
759 uint64_t connectedProofCount{0};
760 uint64_t finalizedProofCount{0};
761 uint64_t connectedNodeCount{0};
762 Amount totalStakes = Amount::zero();
763 Amount connectedStakes = Amount::zero();
764
765 pm.forEachPeer([&](const avalanche::Peer &peer) {
766 CHECK_NONFATAL(peer.proof != nullptr);
767
768 const bool isLocalProof =
769 localProof &&
770 peer.proof->getId() == localProof->getId();
771
772 ++proofCount;
773 const Amount proofStake = peer.proof->getStakedAmount();
774
775 totalStakes += proofStake;
776
777 if (peer.hasFinalized) {
778 ++finalizedProofCount;
779 }
780
781 if (peer.node_count > 0 || isLocalProof) {
782 ++connectedProofCount;
783 connectedStakes += proofStake;
784 }
785
786 connectedNodeCount += peer.node_count + isLocalProof;
787 });
788
789 Amount immatureStakes = Amount::zero();
791 [&](const avalanche::ProofRef &proof) {
792 immatureStakes += proof->getStakedAmount();
793 });
794
795 network.pushKV("proof_count", proofCount);
796 network.pushKV("connected_proof_count", connectedProofCount);
797 network.pushKV("dangling_proof_count",
798 proofCount - connectedProofCount);
799
800 network.pushKV("finalized_proof_count", finalizedProofCount);
801 network.pushKV(
802 "conflicting_proof_count",
803 uint64_t(pm.getConflictingProofPool().countProofs()));
804 network.pushKV(
805 "immature_proof_count",
806 uint64_t(pm.getImmatureProofPool().countProofs()));
807
808 network.pushKV("total_stake_amount", totalStakes);
809 network.pushKV("connected_stake_amount", connectedStakes);
810 network.pushKV("dangling_stake_amount",
811 totalStakes - connectedStakes);
812 network.pushKV("immature_stake_amount", immatureStakes);
813
814 const uint64_t pendingNodes = pm.getPendingNodeCount();
815 network.pushKV("node_count", connectedNodeCount + pendingNodes);
816 network.pushKV("connected_node_count", connectedNodeCount);
817 network.pushKV("pending_node_count", pendingNodes);
818
819 ret.pushKV("network", network);
820 });
821
822 return ret;
823 },
824 };
825}
826
828 return RPCHelpMan{
829 "getavalanchepeerinfo",
830 "Returns data about an avalanche peer as a json array of objects. If "
831 "no proofid is provided, returns data about all the peers.\n",
832 {
834 "The hex encoded avalanche proof identifier."},
835 },
836 RPCResult{
838 "",
839 "",
840 {{
842 "",
843 "",
844 {{
845 {RPCResult::Type::NUM, "avalanche_peerid",
846 "The avalanche internal peer identifier"},
847 {RPCResult::Type::NUM, "availability_score",
848 "The agreggated availability score of this peer's nodes"},
849 {RPCResult::Type::STR_HEX, "proofid",
850 "The avalanche proof id used by this peer"},
851 {RPCResult::Type::STR_HEX, "proof",
852 "The avalanche proof used by this peer"},
853 {RPCResult::Type::NUM, "nodecount",
854 "The number of nodes for this peer"},
856 "node_list",
857 "",
858 {
859 {RPCResult::Type::NUM, "nodeid",
860 "Node id, as returned by getpeerinfo"},
861 }},
862 }},
863 }},
864 },
865 RPCExamples{HelpExampleCli("getavalanchepeerinfo", "") +
866 HelpExampleCli("getavalanchepeerinfo", "\"proofid\"") +
867 HelpExampleRpc("getavalanchepeerinfo", "") +
868 HelpExampleRpc("getavalanchepeerinfo", "\"proofid\"")},
869 [&](const RPCHelpMan &self, const Config &config,
870 const JSONRPCRequest &request) -> UniValue {
871 NodeContext &node = EnsureAnyNodeContext(request.context);
873
874 auto peerToUniv = [](const avalanche::PeerManager &pm,
875 const avalanche::Peer &peer) {
877
878 obj.pushKV("avalanche_peerid", uint64_t(peer.peerid));
879 obj.pushKV("availability_score", peer.availabilityScore);
880 obj.pushKV("proofid", peer.getProofId().ToString());
881 obj.pushKV("proof", peer.proof->ToHex());
882
884 pm.forEachNode(peer, [&](const avalanche::Node &n) {
885 nodes.push_back(n.nodeid);
886 });
887
888 obj.pushKV("nodecount", uint64_t(peer.node_count));
889 obj.pushKV("node_list", nodes);
890
891 return obj;
892 };
893
895
896 avalanche.withPeerManager([&](const avalanche::PeerManager &pm) {
897 // If a proofid is provided, only return the associated peer
898 if (!request.params[0].isNull()) {
899 const avalanche::ProofId proofid =
900 avalanche::ProofId::fromHex(
901 request.params[0].get_str());
902 if (!pm.isBoundToPeer(proofid)) {
903 throw JSONRPCError(RPC_INVALID_PARAMETER,
904 "Proofid not found");
905 }
906
907 pm.forPeer(proofid, [&](const avalanche::Peer &peer) {
908 ret.push_back(peerToUniv(pm, peer));
909 return true;
910 });
911
912 return;
913 }
914
915 // If no proofid is provided, return all the peers
916 pm.forEachPeer([&](const avalanche::Peer &peer) {
917 ret.push_back(peerToUniv(pm, peer));
918 });
919 });
920
921 return ret;
922 },
923 };
924}
925
927 return RPCHelpMan{
928 "getavalancheproofs",
929 "Returns an object containing all tracked proofids.\n",
930 {},
931 RPCResult{
933 "",
934 "",
935 {
937 "valid",
938 "",
939 {
940 {RPCResult::Type::STR_HEX, "proofid",
941 "Avalanche proof id"},
942 }},
944 "conflicting",
945 "",
946 {
947 {RPCResult::Type::STR_HEX, "proofid",
948 "Avalanche proof id"},
949 }},
951 "immature",
952 "",
953 {
954 {RPCResult::Type::STR_HEX, "proofid",
955 "Avalanche proof id"},
956 }},
957 },
958 },
959 RPCExamples{HelpExampleCli("getavalancheproofs", "") +
960 HelpExampleRpc("getavalancheproofs", "")},
961 [&](const RPCHelpMan &self, const Config &config,
962 const JSONRPCRequest &request) -> UniValue {
963 NodeContext &node = EnsureAnyNodeContext(request.context);
965
967 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
968 auto appendProofIds = [&ret](const avalanche::ProofPool &pool,
969 const std::string &key) {
970 UniValue arrOut(UniValue::VARR);
971 for (const avalanche::ProofId &proofid :
972 pool.getProofIds()) {
973 arrOut.push_back(proofid.ToString());
974 }
975 ret.pushKV(key, arrOut);
976 };
977
978 appendProofIds(pm.getValidProofPool(), "valid");
979 appendProofIds(pm.getConflictingProofPool(), "conflicting");
980 appendProofIds(pm.getImmatureProofPool(), "immature");
981 });
982
983 return ret;
984 },
985 };
986}
987
989 return RPCHelpMan{
990 "getstakingreward",
991 "Return a list of possible staking reward winners based on the "
992 "previous "
993 "block hash.\n",
994 {
996 "The previous block hash, hex encoded."},
997 {"recompute", RPCArg::Type::BOOL, RPCArg::Default{false},
998 "Whether to recompute the staking reward winner if there is a "
999 "cached value."},
1000 },
1001 RPCResult{
1003 "",
1004 "",
1005 {
1007 "winner",
1008 "The winning proof",
1009 {
1010 {RPCResult::Type::STR_HEX, "proofid",
1011 "The winning proofid"},
1012 {RPCResult::Type::STR, "asm", "Decoded payout script"},
1014 "Raw payout script in hex format"},
1015 {RPCResult::Type::STR, "type",
1016 "The output type (e.g. " + GetAllOutputTypes() + ")"},
1017 {RPCResult::Type::NUM, "reqSigs",
1018 "The required signatures"},
1020 "addresses",
1021 "",
1022 {
1023 {RPCResult::Type::STR, "address", "eCash address"},
1024 }},
1025 }},
1026 }},
1027 RPCExamples{HelpExampleRpc("getstakingreward", "<blockhash>")},
1028 [&](const RPCHelpMan &self, const Config &config,
1029 const JSONRPCRequest &request) -> UniValue {
1030 const NodeContext &node = EnsureAnyNodeContext(request.context);
1033
1034 const BlockHash blockhash(
1035 ParseHashV(request.params[0], "blockhash"));
1036
1037 const CBlockIndex *pprev;
1038 {
1039 LOCK(cs_main);
1040 pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1041 }
1042
1043 if (!pprev) {
1044 throw JSONRPCError(
1046 strprintf("Block not found: %s\n", blockhash.ToString()));
1047 }
1048
1050 config.GetChainParams().GetConsensus(), pprev)) {
1051 throw JSONRPCError(
1053 strprintf(
1054 "Staking rewards are not activated for block %s\n",
1055 blockhash.ToString()));
1056 }
1057
1058 if (!request.params[1].isNull() && request.params[1].get_bool()) {
1059 // Force recompute the staking reward winner by first erasing
1060 // the cached entry if any
1061 avalanche.eraseStakingRewardWinner(blockhash);
1062 }
1063
1064 if (!avalanche.computeStakingReward(pprev)) {
1065 throw JSONRPCError(
1067 strprintf("Unable to determine a staking reward winner "
1068 "for block %s\n",
1069 blockhash.ToString()));
1070 }
1071
1072 std::vector<std::pair<avalanche::ProofId, CScript>> winners;
1073 if (!avalanche.getStakingRewardWinners(blockhash, winners)) {
1074 throw JSONRPCError(
1076 strprintf("Unable to retrieve the staking reward winner "
1077 "for block %s\n",
1078 blockhash.ToString()));
1079 }
1080
1081 UniValue winnersArr(UniValue::VARR);
1082 for (auto &winner : winners) {
1083 UniValue stakingRewardsObj(UniValue::VOBJ);
1084 ScriptPubKeyToUniv(winner.second, stakingRewardsObj,
1085 /*fIncludeHex=*/true);
1086 stakingRewardsObj.pushKV("proofid", winner.first.GetHex());
1087 winnersArr.push_back(stakingRewardsObj);
1088 }
1089
1090 return winnersArr;
1091 },
1092 };
1093}
1094
1096 return RPCHelpMan{
1097 "setstakingreward",
1098 "Set the staking reward winner for the given previous block hash.\n",
1099 {
1101 "The previous block hash, hex encoded."},
1103 "The payout script for the staking reward, hex encoded."},
1104 {"append", RPCArg::Type::BOOL, RPCArg::Default{false},
1105 "Append to the list of possible winners instead of replacing."},
1106 },
1108 "Whether the payout script was set or not"},
1110 HelpExampleRpc("setstakingreward", "<blockhash> <payout script>")},
1111 [&](const RPCHelpMan &self, const Config &config,
1112 const JSONRPCRequest &request) -> UniValue {
1113 const NodeContext &node = EnsureAnyNodeContext(request.context);
1116
1117 const BlockHash blockhash(
1118 ParseHashV(request.params[0], "blockhash"));
1119
1120 const CBlockIndex *pprev;
1121 {
1122 LOCK(cs_main);
1123 pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1124 }
1125
1126 if (!pprev) {
1127 throw JSONRPCError(
1129 strprintf("Block not found: %s\n", blockhash.ToString()));
1130 }
1131
1133 config.GetChainParams().GetConsensus(), pprev)) {
1134 throw JSONRPCError(
1136 strprintf(
1137 "Staking rewards are not activated for block %s\n",
1138 blockhash.ToString()));
1139 }
1140
1141 const std::vector<uint8_t> data =
1142 ParseHex(request.params[1].get_str());
1143 CScript payoutScript(data.begin(), data.end());
1144
1145 std::vector<CScript> payoutScripts;
1146
1147 if (!request.params[2].isNull() && request.params[2].get_bool()) {
1148 // Append mode, initialize our list with the current winners
1149 // and the new one will be added to the back of that list. If
1150 // there is no winner the list will remain empty.
1151 avalanche.getStakingRewardWinners(blockhash, payoutScripts);
1152 }
1153
1154 if (std::find(payoutScripts.begin(), payoutScripts.end(),
1155 payoutScript) != payoutScripts.end()) {
1156 throw JSONRPCError(
1158 strprintf(
1159 "Staking rewards winner is already set for block %s\n",
1160 blockhash.ToString()));
1161 }
1162
1163 payoutScripts.push_back(std::move(payoutScript));
1164
1165 // This will return true upon insertion or false upon replacement.
1166 // We want to convey the success of the RPC, so we always return
1167 // true.
1168 avalanche.setStakingRewardWinners(pprev, payoutScripts);
1169 return true;
1170 },
1171 };
1172}
1173
1175 return RPCHelpMan{
1176 "getremoteproofs",
1177 "Get the list of remote proofs for the given node id.\n",
1178 {
1180 "The node identifier."},
1181 },
1182 RPCResult{
1184 "proofs",
1185 "",
1186 {{
1188 "proof",
1189 "",
1190 {{
1191 {RPCResult::Type::STR_HEX, "proofid",
1192 "The hex encoded proof identifier."},
1193 {RPCResult::Type::BOOL, "present",
1194 "Whether the node has the proof."},
1195 {RPCResult::Type::NUM, "last_update",
1196 "The last time this proof status was updated."},
1197 }},
1198 }},
1199 },
1200 RPCExamples{HelpExampleRpc("getremoteproofs", "<nodeid>")},
1201 [&](const RPCHelpMan &self, const Config &config,
1202 const JSONRPCRequest &request) -> UniValue {
1203 NodeContext &node = EnsureAnyNodeContext(request.context);
1205
1206 const NodeId nodeid = request.params[0].getInt<int64_t>();
1207 auto remoteProofs = avalanche.withPeerManager(
1208 [nodeid](const avalanche::PeerManager &pm) {
1209 return pm.getRemoteProofs(nodeid);
1210 });
1211
1212 UniValue arrOut(UniValue::VARR);
1213
1214 for (const auto &remoteProof : remoteProofs) {
1216 obj.pushKV("proofid", remoteProof.proofid.ToString());
1217 obj.pushKV("present", remoteProof.present);
1218 obj.pushKV("last_update", remoteProof.lastUpdate.count());
1219
1220 arrOut.push_back(obj);
1221 }
1222
1223 return arrOut;
1224 },
1225 };
1226}
1227
1229 return RPCHelpMan{
1230 "getrawavalancheproof",
1231 "Lookup for a known avalanche proof by id.\n",
1232 {
1234 "The hex encoded avalanche proof identifier."},
1235 },
1236 RPCResult{
1238 "",
1239 "",
1240 {{
1241 {RPCResult::Type::STR_HEX, "proof",
1242 "The hex encoded proof matching the identifier."},
1243 {RPCResult::Type::BOOL, "immature",
1244 "Whether the proof has immature utxos."},
1245 {RPCResult::Type::BOOL, "boundToPeer",
1246 "Whether the proof is bound to an avalanche peer."},
1247 {RPCResult::Type::BOOL, "conflicting",
1248 "Whether the proof has a conflicting UTXO with an avalanche "
1249 "peer."},
1250 {RPCResult::Type::BOOL, "finalized",
1251 "Whether the proof is finalized by vote."},
1252 }},
1253 },
1254 RPCExamples{HelpExampleRpc("getrawavalancheproof", "<proofid>")},
1255 [&](const RPCHelpMan &self, const Config &config,
1256 const JSONRPCRequest &request) -> UniValue {
1257 NodeContext &node = EnsureAnyNodeContext(request.context);
1259
1260 const avalanche::ProofId proofid =
1261 avalanche::ProofId::fromHex(request.params[0].get_str());
1262
1263 bool isImmature = false;
1264 bool isBoundToPeer = false;
1265 bool conflicting = false;
1266 bool finalized = false;
1267 auto proof = avalanche.withPeerManager(
1268 [&](const avalanche::PeerManager &pm) {
1269 isImmature = pm.isImmature(proofid);
1270 isBoundToPeer = pm.isBoundToPeer(proofid);
1271 conflicting = pm.isInConflictingPool(proofid);
1272 finalized =
1273 pm.forPeer(proofid, [&](const avalanche::Peer &p) {
1274 return p.hasFinalized;
1275 });
1276 return pm.getProof(proofid);
1277 });
1278
1279 if (!proof) {
1280 throw JSONRPCError(RPC_INVALID_PARAMETER, "Proof not found");
1281 }
1282
1284
1286 ss << *proof;
1287 ret.pushKV("proof", HexStr(ss));
1288 ret.pushKV("immature", isImmature);
1289 ret.pushKV("boundToPeer", isBoundToPeer);
1290 ret.pushKV("conflicting", conflicting);
1291 ret.pushKV("finalized", finalized);
1292
1293 return ret;
1294 },
1295 };
1296}
1297
1299 return RPCHelpMan{
1300 "invalidateavalancheproof",
1301 "Reject a known avalanche proof by id.\n",
1302 {
1304 "The hex encoded avalanche proof identifier."},
1305 },
1306 RPCResult{
1308 "success",
1309 "",
1310 },
1311 RPCExamples{HelpExampleRpc("invalidateavalancheproof", "<proofid>")},
1312 [&](const RPCHelpMan &self, const Config &config,
1313 const JSONRPCRequest &request) -> UniValue {
1314 NodeContext &node = EnsureAnyNodeContext(request.context);
1316
1317 const avalanche::ProofId proofid =
1318 avalanche::ProofId::fromHex(request.params[0].get_str());
1319
1320 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
1321 if (!pm.exists(proofid) && !pm.isDangling(proofid)) {
1322 throw JSONRPCError(RPC_INVALID_PARAMETER,
1323 "Proof not found");
1324 }
1325
1326 if (!pm.rejectProof(
1327 proofid,
1329 throw JSONRPCError(RPC_INTERNAL_ERROR,
1330 "Failed to reject the proof");
1331 }
1332
1333 pm.setInvalid(proofid);
1334 });
1335
1336 if (avalanche.isRecentlyFinalized(proofid)) {
1337 // If the proof was previously finalized, clear the status.
1338 // Because there is no way to selectively delete an entry from a
1339 // Bloom filter, we have to clear the whole filter which could
1340 // cause extra voting rounds.
1341 avalanche.clearFinalizedItems();
1342 }
1343
1344 return true;
1345 },
1346 };
1347}
1348
1350 return RPCHelpMan{
1351 "isfinalblock",
1352 "Check if a block has been finalized by avalanche votes.\n",
1353 {
1355 "The hash of the block."},
1356 },
1358 "Whether the block has been finalized by avalanche votes."},
1359 RPCExamples{HelpExampleRpc("isfinalblock", "<block hash>") +
1360 HelpExampleCli("isfinalblock", "<block hash>")},
1361 [&](const RPCHelpMan &self, const Config &config,
1362 const JSONRPCRequest &request) -> UniValue {
1363 NodeContext &node = EnsureAnyNodeContext(request.context);
1365
1366 if (!avalanche.isQuorumEstablished()) {
1368 "Avalanche is not ready to poll yet.");
1369 }
1370
1371 ChainstateManager &chainman = EnsureAnyChainman(request.context);
1372 const BlockHash blockhash(
1373 ParseHashV(request.params[0], "blockhash"));
1374 const CBlockIndex *pindex;
1375
1376 {
1377 LOCK(cs_main);
1378 pindex = chainman.m_blockman.LookupBlockIndex(blockhash);
1379
1380 if (!pindex) {
1382 "Block not found");
1383 }
1384 }
1385
1386 return chainman.ActiveChainstate().IsBlockAvalancheFinalized(
1387 pindex);
1388 },
1389 };
1390}
1391
1393 return RPCHelpMan{
1394 "isfinaltransaction",
1395 "Check if a transaction has been finalized by avalanche votes.\n",
1396 {
1398 "The id of the transaction."},
1400 "The block in which to look for the transaction"},
1401 },
1402 RPCResult{
1403 RPCResult::Type::BOOL, "success",
1404 "Whether the transaction has been finalized by avalanche votes."},
1405 RPCExamples{HelpExampleRpc("isfinaltransaction", "<txid> <blockhash>") +
1406 HelpExampleCli("isfinaltransaction", "<txid> <blockhash>")},
1407 [&](const RPCHelpMan &self, const Config &config,
1408 const JSONRPCRequest &request) -> UniValue {
1409 const NodeContext &node = EnsureAnyNodeContext(request.context);
1411 const CTxMemPool &mempool = EnsureMemPool(node);
1413
1414 const TxId txid = TxId(ParseHashV(request.params[0], "txid"));
1415 CBlockIndex *pindex = nullptr;
1416
1417 if (!request.params[1].isNull()) {
1418 const BlockHash blockhash(
1419 ParseHashV(request.params[1], "blockhash"));
1420
1421 LOCK(cs_main);
1422 pindex = chainman.m_blockman.LookupBlockIndex(blockhash);
1423 if (!pindex) {
1425 "Block not found");
1426 }
1427 }
1428
1429 bool f_txindex_ready = false;
1430 if (g_txindex && !pindex) {
1431 f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain();
1432 }
1433
1434 BlockHash hash_block;
1436 pindex, &mempool, txid, hash_block, chainman.m_blockman);
1437
1438 if (!avalanche.isQuorumEstablished()) {
1440 "Avalanche is not ready to poll yet.");
1441 }
1442
1443 if (!tx) {
1444 std::string errmsg;
1445 if (pindex) {
1446 if (WITH_LOCK(::cs_main,
1447 return !pindex->nStatus.hasData())) {
1449 "Block data not downloaded yet.");
1450 }
1451 errmsg = "No such transaction found in the provided block.";
1452 } else if (!g_txindex) {
1453 errmsg = "No such transaction. Use -txindex or provide a "
1454 "block hash to enable blockchain transaction "
1455 "queries.";
1456 } else if (!f_txindex_ready) {
1457 errmsg = "No such transaction. Blockchain transactions are "
1458 "still in the process of being indexed.";
1459 } else {
1460 errmsg = "No such mempool or blockchain transaction.";
1461 }
1463 }
1464
1465 if (!pindex) {
1466 LOCK(cs_main);
1467 pindex = chainman.m_blockman.LookupBlockIndex(hash_block);
1468 }
1469
1470 if (!tx) {
1471 // Tx not found, we should have raised an error at this stage
1472 return false;
1473 }
1474
1475 if (mempool.isAvalancheFinalized(txid)) {
1476 // The transaction is finalized
1477 return true;
1478 }
1479
1480 // Return true if the tx is in a finalized block
1481 return !node.mempool->exists(txid) &&
1482 chainman.ActiveChainstate().IsBlockAvalancheFinalized(
1483 pindex);
1484 },
1485 };
1486}
1487
1489 return RPCHelpMan{
1490 "reconsideravalancheproof",
1491 "Reconsider a known avalanche proof.\n",
1492 {
1494 "The hex encoded avalanche proof."},
1495 },
1496 RPCResult{
1498 "success",
1499 "Whether the proof has been successfully registered.",
1500 },
1501 RPCExamples{HelpExampleRpc("reconsideravalancheproof", "<proof hex>")},
1502 [&](const RPCHelpMan &self, const Config &config,
1503 const JSONRPCRequest &request) -> UniValue {
1504 auto proof = RCUPtr<avalanche::Proof>::make();
1505
1506 NodeContext &node = EnsureAnyNodeContext(request.context);
1508
1509 // Verify the proof. Note that this is redundant with the
1510 // verification done when adding the proof to the pool, but we get a
1511 // chance to give a better error message.
1512 verifyProofOrThrow(node, *proof, request.params[0].get_str());
1513
1514 // There is no way to selectively clear the invalidation status of
1515 // a single proof, so we clear the whole Bloom filter. This could
1516 // cause extra voting rounds.
1517 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
1518 if (pm.isInvalid(proof->getId())) {
1519 pm.clearAllInvalid();
1520 }
1521 });
1522
1523 // Add the proof to the pool if we don't have it already. Since the
1524 // proof verification has already been done, a failure likely
1525 // indicates that there already is a proof with conflicting utxos.
1527 if (!registerProofIfNeeded(avalanche, proof, state)) {
1529 strprintf("%s (%s)\n",
1530 state.GetRejectReason(),
1531 state.GetDebugMessage()));
1532 }
1533
1534 return avalanche.withPeerManager(
1535 [&](const avalanche::PeerManager &pm) {
1536 return pm.isBoundToPeer(proof->getId());
1537 });
1538 },
1539 };
1540}
1541
1543 return RPCHelpMan{
1544 "sendavalancheproof",
1545 "Broadcast an avalanche proof.\n",
1546 {
1548 "The avalanche proof to broadcast."},
1549 },
1551 "Whether the proof was sent successfully or not."},
1552 RPCExamples{HelpExampleRpc("sendavalancheproof", "<proof>")},
1553 [&](const RPCHelpMan &self, const Config &config,
1554 const JSONRPCRequest &request) -> UniValue {
1555 auto proof = RCUPtr<avalanche::Proof>::make();
1556
1557 NodeContext &node = EnsureAnyNodeContext(request.context);
1559
1560 // Verify the proof. Note that this is redundant with the
1561 // verification done when adding the proof to the pool, but we get a
1562 // chance to give a better error message.
1563 verifyProofOrThrow(node, *proof, request.params[0].get_str());
1564
1565 // Add the proof to the pool if we don't have it already. Since the
1566 // proof verification has already been done, a failure likely
1567 // indicates that there already is a proof with conflicting utxos.
1568 const avalanche::ProofId &proofid = proof->getId();
1570 if (!registerProofIfNeeded(avalanche, proof, state)) {
1572 strprintf("%s (%s)\n",
1573 state.GetRejectReason(),
1574 state.GetDebugMessage()));
1575 }
1576
1577 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
1578 pm.addUnbroadcastProof(proofid);
1579 });
1580
1581 if (node.peerman) {
1582 node.peerman->RelayProof(proofid);
1583 }
1584
1585 return true;
1586 },
1587 };
1588}
1589
1591 return RPCHelpMan{
1592 "verifyavalancheproof",
1593 "Verify an avalanche proof is valid and return the error otherwise.\n",
1594 {
1596 "Proof to verify."},
1597 },
1599 "Whether the proof is valid or not."},
1600 RPCExamples{HelpExampleRpc("verifyavalancheproof", "\"<proof>\"")},
1601 [&](const RPCHelpMan &self, const Config &config,
1602 const JSONRPCRequest &request) -> UniValue {
1603 avalanche::Proof proof;
1604 verifyProofOrThrow(EnsureAnyNodeContext(request.context), proof,
1605 request.params[0].get_str());
1606
1607 return true;
1608 },
1609 };
1610}
1611
1613 return RPCHelpMan{
1614 "verifyavalanchedelegation",
1615 "Verify an avalanche delegation is valid and return the error "
1616 "otherwise.\n",
1617 {
1619 "The avalanche proof delegation to verify."},
1620 },
1622 "Whether the delegation is valid or not."},
1623 RPCExamples{HelpExampleRpc("verifyavalanchedelegation", "\"<proof>\"")},
1624 [&](const RPCHelpMan &self, const Config &config,
1625 const JSONRPCRequest &request) -> UniValue {
1626 avalanche::Delegation delegation;
1627 CPubKey dummy;
1628 verifyDelegationOrThrow(delegation, request.params[0].get_str(),
1629 dummy);
1630
1631 return true;
1632 },
1633 };
1634}
1635
1637 return RPCHelpMan{
1638 "setflakyproof",
1639 "Add or remove a proofid from the flaky list. This means that an "
1640 "additional staking reward winner will be accepted if this proof is "
1641 "the selected one.\n",
1642 {
1644 "The avalanche proof id."},
1646 "Whether to add (true) or remove (false) the proof from the flaky "
1647 "list"},
1648 },
1650 "Whether the addition/removal is successful."},
1651 RPCExamples{HelpExampleRpc("setflakyproof", "\"<proofid>\" true")},
1652 [&](const RPCHelpMan &self, const Config &config,
1653 const JSONRPCRequest &request) -> UniValue {
1654 NodeContext &node = EnsureAnyNodeContext(request.context);
1657
1658 const auto proofid =
1659 avalanche::ProofId::fromHex(request.params[0].get_str());
1660 const bool addNotRemove = request.params[1].get_bool();
1661
1662 if (avalanche.withPeerManager(
1663 [&proofid, addNotRemove](avalanche::PeerManager &pm) {
1664 if (addNotRemove) {
1665 return pm.setFlaky(proofid);
1666 }
1667 return pm.unsetFlaky(proofid);
1668 })) {
1669 const CBlockIndex *pprev =
1670 WITH_LOCK(cs_main, return chainman.ActiveTip());
1671 // Force recompute the staking reward winner by first erasing
1672 // the cached entry if any
1673 avalanche.eraseStakingRewardWinner(pprev->GetBlockHash());
1674 return avalanche.computeStakingReward(pprev);
1675 }
1676
1677 return false;
1678 }};
1679}
1680
1682 return RPCHelpMan{
1683 "getflakyproofs",
1684 "List the flaky proofs (set via setflakyproof).\n",
1685 {},
1686 RPCResult{
1688 "flaky_proofs",
1689 "",
1690 {{
1692 "proof",
1693 "",
1694 {{
1695 {RPCResult::Type::STR_HEX, "proofid",
1696 "The hex encoded proof identifier."},
1697 {RPCResult::Type::STR_AMOUNT, "staked_amount",
1698 "The proof stake amount, only present if the proof is "
1699 "known."},
1701 "payout",
1702 "The proof payout script, only present if the proof is "
1703 "known.",
1704 {
1705 {RPCResult::Type::STR, "asm", "Decoded payout script"},
1707 "Raw payout script in hex format"},
1708 {RPCResult::Type::STR, "type",
1709 "The output type (e.g. " + GetAllOutputTypes() + ")"},
1710 {RPCResult::Type::NUM, "reqSigs",
1711 "The required signatures"},
1713 "addresses",
1714 "",
1715 {
1716 {RPCResult::Type::STR, "address",
1717 "eCash address"},
1718 }},
1719 }},
1720 }},
1721 }},
1722 },
1723 RPCExamples{HelpExampleRpc("getflakyproofs", "")},
1724 [&](const RPCHelpMan &self, const Config &config,
1725 const JSONRPCRequest &request) -> UniValue {
1726 NodeContext &node = EnsureAnyNodeContext(request.context);
1728
1729 UniValue flakyProofs(UniValue::VARR);
1730 avalanche.withPeerManager([&flakyProofs](
1732 pm.forEachFlakyProof([&](const avalanche::ProofId &proofid) {
1733 UniValue flakyProof(UniValue::VOBJ);
1734 flakyProof.pushKV("proofid", proofid.GetHex());
1735
1736 const auto proof = pm.getProof(proofid);
1737 if (proof) {
1738 flakyProof.pushKV("staked_amount",
1739 proof->getStakedAmount());
1740 UniValue payout(UniValue::VOBJ);
1741 ScriptPubKeyToUniv(proof->getPayoutScript(), payout,
1742 /*fIncludeHex=*/true);
1743 flakyProof.pushKV("payout", payout);
1744 }
1745
1746 flakyProofs.push_back(flakyProof);
1747 });
1748 });
1749
1750 return flakyProofs;
1751 }};
1752}
1753
1755 // clang-format off
1756 static const CRPCCommand commands[] = {
1757 // category actor (function)
1758 // ----------------- --------------------
1759 { "avalanche", getavalanchekey, },
1760 { "avalanche", addavalanchenode, },
1761 { "avalanche", buildavalancheproof, },
1762 { "avalanche", decodeavalancheproof, },
1763 { "avalanche", delegateavalancheproof, },
1764 { "avalanche", decodeavalanchedelegation, },
1765 { "avalanche", getavalancheinfo, },
1766 { "avalanche", getavalanchepeerinfo, },
1767 { "avalanche", getavalancheproofs, },
1768 { "avalanche", getstakingreward, },
1769 { "avalanche", setstakingreward, },
1770 { "avalanche", getremoteproofs, },
1771 { "avalanche", getrawavalancheproof, },
1772 { "avalanche", invalidateavalancheproof, },
1773 { "avalanche", isfinalblock, },
1774 { "avalanche", isfinaltransaction, },
1775 { "avalanche", reconsideravalancheproof, },
1776 { "avalanche", sendavalancheproof, },
1777 { "avalanche", verifyavalancheproof, },
1778 { "avalanche", verifyavalanchedelegation, },
1779 { "avalanche", setflakyproof, },
1780 { "avalanche", getflakyproofs, },
1781 };
1782 // clang-format on
1783
1784 for (const auto &c : commands) {
1785 t.appendCommand(c.name, &c);
1786 }
1787}
static RPCHelpMan buildavalancheproof()
Definition: avalanche.cpp:205
static RPCHelpMan invalidateavalancheproof()
Definition: avalanche.cpp:1298
static RPCHelpMan delegateavalancheproof()
Definition: avalanche.cpp:461
static RPCHelpMan getremoteproofs()
Definition: avalanche.cpp:1174
static RPCHelpMan decodeavalanchedelegation()
Definition: avalanche.cpp:533
static RPCHelpMan sendavalancheproof()
Definition: avalanche.cpp:1542
static RPCHelpMan getavalancheproofs()
Definition: avalanche.cpp:926
static void verifyDelegationOrThrow(avalanche::Delegation &dg, const std::string &dgHex, CPubKey &auth)
Definition: avalanche.cpp:80
static RPCHelpMan getrawavalancheproof()
Definition: avalanche.cpp:1228
static void verifyProofOrThrow(const NodeContext &node, avalanche::Proof &proof, const std::string &proofHex)
Definition: avalanche.cpp:94
void RegisterAvalancheRPCCommands(CRPCTable &t)
Definition: avalanche.cpp:1754
static RPCHelpMan getavalanchekey()
Definition: avalanche.cpp:33
static RPCHelpMan addavalanchenode()
Definition: avalanche.cpp:120
static CPubKey ParsePubKey(const UniValue &param)
Definition: avalanche.cpp:49
static RPCHelpMan verifyavalanchedelegation()
Definition: avalanche.cpp:1612
static RPCHelpMan setflakyproof()
Definition: avalanche.cpp:1636
static RPCHelpMan setstakingreward()
Definition: avalanche.cpp:1095
static RPCHelpMan getflakyproofs()
Definition: avalanche.cpp:1681
static RPCHelpMan isfinalblock()
Definition: avalanche.cpp:1349
static RPCHelpMan reconsideravalancheproof()
Definition: avalanche.cpp:1488
static RPCHelpMan isfinaltransaction()
Definition: avalanche.cpp:1392
static RPCHelpMan getstakingreward()
Definition: avalanche.cpp:988
static bool registerProofIfNeeded(const avalanche::Processor &avalanche, avalanche::ProofRef proof, avalanche::ProofRegistrationState &state)
Definition: avalanche.cpp:61
static RPCHelpMan getavalanchepeerinfo()
Definition: avalanche.cpp:827
static RPCHelpMan verifyavalancheproof()
Definition: avalanche.cpp:1590
static RPCHelpMan getavalancheinfo()
Definition: avalanche.cpp:616
static RPCHelpMan decodeavalancheproof()
Definition: avalanche.cpp:336
#define CHECK_NONFATAL(condition)
Identity function.
Definition: check.h:53
#define Assert(val)
Identity function.
Definition: check.h:84
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
BlockHash GetBlockHash() const
Definition: blockindex.h:146
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:177
An encapsulated secp256k1 private key.
Definition: key.h:28
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:97
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:210
Information about a peer.
Definition: net.h:460
An encapsulated public key.
Definition: pubkey.h:31
static constexpr unsigned int COMPRESSED_SIZE
Definition: pubkey.h:37
static constexpr unsigned int SIZE
secp256k1:
Definition: pubkey.h:36
RPC command dispatcher.
Definition: server.h:194
void appendCommand(const std::string &name, const CRPCCommand *pcmd)
Appends a CRPCCommand to the dispatch table.
Definition: server.cpp:327
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:212
bool isAvalancheFinalized(const TxId &txid) const
Definition: txmempool.h:513
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1219
SnapshotCompletionResult MaybeCompleteSnapshotValidation(std::function< void(bilingual_str)> shutdown_fnc=[](bilingual_str msg) { AbortNode(msg.original, msg);}) EXCLUSIVE_LOCKS_REQUIRED(Chainstate & ActiveChainstate() const
Once the background validation chainstate has reached the height which is the base of the UTXO snapsh...
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1351
Definition: config.h:19
static RCUPtr make(Args &&...args)
Construct a new object that is owned by the pointer.
Definition: rcu.h:112
void push_back(UniValue val)
Definition: univalue.cpp:96
const std::string & get_str() const
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:229
@ VOBJ
Definition: univalue.h:31
@ VSTR
Definition: univalue.h:33
@ VARR
Definition: univalue.h:32
@ VNUM
Definition: univalue.h:34
bool isNull() const
Definition: univalue.h:104
size_t size() const
Definition: univalue.h:92
Int getInt() const
Definition: univalue.h:157
const UniValue & get_array() const
bool exists(const std::string &key) const
Definition: univalue.h:99
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:115
bool get_bool() const
bool IsValid() const
Definition: validation.h:119
std::string GetRejectReason() const
Definition: validation.h:123
std::string GetDebugMessage() const
Definition: validation.h:124
std::string ToString() const
Definition: validation.h:125
ProofId getProofId() const
Definition: delegation.cpp:56
const std::vector< Level > & getLevels() const
Definition: delegation.h:64
static bool FromHex(Delegation &dg, const std::string &dgHex, bilingual_str &errorOut)
Definition: delegation.cpp:16
bool verify(DelegationState &state, CPubKey &auth) const
Definition: delegation.cpp:73
const CPubKey & getProofMaster() const
Definition: delegation.h:62
const DelegationId & getId() const
Definition: delegation.h:60
const CPubKey & getDelegatedPubkey() const
Definition: delegation.cpp:60
const LimitedProofId & getLimitedProofId() const
Definition: delegation.h:61
std::vector< RemoteProof > getRemoteProofs(const NodeId nodeid) const
bool isDangling(const ProofId &proofid) const
bool unsetFlaky(const ProofId &proofid)
bool exists(const ProofId &proofid) const
Return true if the (valid) proof exists, but only for non-dangling proofs.
Definition: peermanager.h:406
const ProofPool & getValidProofPool() const
Definition: peermanager.h:505
bool forPeer(const ProofId &proofid, Callable &&func) const
Definition: peermanager.h:414
bool addNode(NodeId nodeid, const ProofId &proofid)
Node API.
Definition: peermanager.cpp:31
void addUnbroadcastProof(const ProofId &proofid)
Proof broadcast API.
bool isBoundToPeer(const ProofId &proofid) const
size_t getPendingNodeCount() const
Definition: peermanager.h:314
const ProofPool & getImmatureProofPool() const
Definition: peermanager.h:509
void forEachPeer(Callable &&func) const
Definition: peermanager.h:420
void setInvalid(const ProofId &proofid)
void forEachNode(const Peer &peer, Callable &&func) const
Definition: peermanager.h:340
const Amount & getStakeUtxoDustThreshold() const
Definition: peermanager.h:525
void forEachFlakyProof(Callable &&func) const
Definition: peermanager.h:451
bool isInvalid(const ProofId &proofid) const
bool isImmature(const ProofId &proofid) const
bool rejectProof(const ProofId &proofid, RejectionMode mode=RejectionMode::DEFAULT)
const ProofPool & getConflictingProofPool() const
Definition: peermanager.h:506
bool isInConflictingPool(const ProofId &proofid) const
ProofRef getProof(const ProofId &proofid) const
bool registerProof(const ProofRef &proof, ProofRegistrationState &registrationState, RegistrationMode mode=RegistrationMode::DEFAULT)
bool addUTXO(COutPoint utxo, Amount amount, uint32_t height, bool is_coinbase, CKey key)
int64_t getExpirationTime() const
Definition: proof.h:163
static bool FromHex(Proof &proof, const std::string &hexProof, bilingual_str &errorOut)
Definition: proof.cpp:51
bool verify(const Amount &stakeUtxoDustThreshold, ProofValidationState &state) const
Definition: proof.cpp:119
Amount getStakedAmount() const
Definition: proof.cpp:104
const CPubKey & getMaster() const
Definition: proof.h:164
uint64_t getSequence() const
Definition: proof.h:162
const LimitedProofId & getLimitedId() const
Definition: proof.h:170
const SchnorrSig & getSignature() const
Definition: proof.h:167
const CScript & getPayoutScript() const
Definition: proof.h:166
uint32_t getScore() const
Definition: proof.h:174
const ProofId & getId() const
Definition: proof.h:169
const std::vector< SignedStake > & getStakes() const
Definition: proof.h:165
Map a proof to each utxo.
Definition: proofpool.h:57
size_t countProofs() const
Definition: proofpool.cpp:129
void forEachProof(Callable &&func) const
Definition: proofpool.h:118
ProofIdSet getProofIds() const
Definition: proofpool.cpp:101
std::string ToString() const
Definition: uint256.h:80
std::string GetHex() const
Definition: uint256.cpp:16
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
void ScriptPubKeyToUniv(const CScript &scriptPubKey, UniValue &out, bool fIncludeHex)
Definition: core_write.cpp:190
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
std::string EncodeDestination(const CTxDestination &dest, const Config &config)
Definition: key_io.cpp:167
CTxDestination DecodeDestination(const std::string &addr, const CChainParams &params)
Definition: key_io.cpp:174
CKey DecodeSecret(const std::string &str)
Definition: key_io.cpp:77
bool error(const char *fmt, const Args &...args)
Definition: logging.h:226
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:40
Definition: init.h:28
CTransactionRef GetTransaction(const CBlockIndex *const block_index, const CTxMemPool *const mempool, const TxId &txid, BlockHash &hashBlock, const BlockManager &blockman)
Return transaction with a given txid.
int64_t NodeId
Definition: nodeid.h:10
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:315
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:58
@ RPC_MISC_ERROR
General application defined errors std::exception thrown in command handling.
Definition: protocol.h:38
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:46
@ RPC_INTERNAL_ERROR
Definition: protocol.h:33
@ RPC_DESERIALIZATION_ERROR
Error parsing or validating structure in raw format.
Definition: protocol.h:50
@ RPC_INVALID_ADDRESS_OR_KEY
Invalid address or key.
Definition: protocol.h:42
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:150
Amount AmountFromValue(const UniValue &value)
Definition: util.cpp:55
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:167
std::string GetAllOutputTypes()
Definition: util.cpp:305
CPubKey HexToPubKey(const std::string &hex_in)
Definition: util.cpp:191
uint256 ParseHashO(const UniValue &o, std::string strKey)
Definition: util.cpp:90
uint256 ParseHashV(const UniValue &v, std::string strName)
Utilities: convert hex-encoded values (throws error if not hex).
Definition: util.cpp:73
void RPCTypeCheckObj(const UniValue &o, const std::map< std::string, UniValueType > &typesExpected, bool fAllowNull, bool fStrict)
Check for expected keys/value types in an Object.
Definition: util.cpp:26
@ SER_NETWORK
Definition: serialize.h:152
ChainstateManager & EnsureAnyChainman(const std::any &context)
Definition: server_util.cpp:59
NodeContext & EnsureAnyNodeContext(const std::any &context)
Definition: server_util.cpp:21
CTxMemPool & EnsureMemPool(const NodeContext &node)
Definition: server_util.cpp:29
ChainstateManager & EnsureChainman(const NodeContext &node)
Definition: server_util.cpp:52
avalanche::Processor & EnsureAvalanche(const NodeContext &node)
Definition: server_util.cpp:81
bool IsStakingRewardsActivated(const Consensus::Params &params, const CBlockIndex *pprev)
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:158
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:260
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:240
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:85
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
static const Currency & get()
Definition: amount.cpp:18
std::string ticker
Definition: amount.h:150
@ STR_HEX
Special type that is a STR with only hex chars.
@ AMOUNT
Special type representing a floating point amount (can be either NUM or STR)
@ OMITTED
The arg is optional for one of two reasons:
@ NO
Required arg.
@ STR_HEX
Special string with only hex chars.
@ STR_AMOUNT
Special string to represent a floating point amount.
A TxId is the identifier of a transaction.
Definition: txid.h:14
NodeId nodeid
Definition: node.h:21
uint32_t node_count
Definition: peermanager.h:87
ProofRef proof
Definition: peermanager.h:89
static ProofId fromHex(const std::string &str)
Definition: proofid.h:21
Bilingual messages:
Definition: translation.h:17
NodeContext struct containing references to chain state and connection state.
Definition: context.h:43
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
std::unique_ptr< TxIndex > g_txindex
The global transaction index, used in GetTransaction. May be null.
Definition: txindex.cpp:16
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::string EncodeBase64(Span< const uint8_t > input)
template std::vector< std::byte > ParseHex(std::string_view)
bool IsHex(std::string_view str)
Returns true if each character in str is a hex character, and has an even number of hex digits.
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11