Bitcoin ABC 0.26.3
P2P Digital Currency
Loading...
Searching...
No Matches
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>
14#include <common/args.h>
15#include <config.h>
16#include <core_io.h>
17#include <index/txindex.h>
18#include <key_io.h>
19#include <net_processing.h>
20#include <node/context.h>
22#include <rpc/blockchain.h>
23#include <rpc/server.h>
24#include <rpc/server_util.h>
25#include <rpc/util.h>
26#include <util/strencodings.h>
27#include <util/translation.h>
28
29#include <univalue.h>
30
33
35 return RPCHelpMan{
36 "getavalanchekey",
37 "Returns the key used to sign avalanche messages.\n",
38 {},
40 RPCExamples{HelpExampleRpc("getavalanchekey", "")},
41 [&](const RPCHelpMan &self, const Config &config,
42 const JSONRPCRequest &request) -> UniValue {
43 NodeContext &node = EnsureAnyNodeContext(request.context);
45 return HexStr(avalanche.getSessionPubKey());
46 },
47 };
48}
49
51 const std::string keyHex = param.get_str();
52 if ((keyHex.length() != 2 * CPubKey::COMPRESSED_SIZE &&
53 keyHex.length() != 2 * CPubKey::SIZE) ||
54 !IsHex(keyHex)) {
56 strprintf("Invalid public key: %s\n", keyHex));
57 }
58
59 return HexToPubKey(keyHex);
60}
61
65 auto localProof = avalanche.getLocalProof();
66 if (localProof && localProof->getId() == proof->getId()) {
67 return true;
68 }
69
70 return avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
71 return pm.getProof(proof->getId()) || pm.registerProof(proof, state);
72 });
73}
74
76 avalanche::ProofRef proof) {
78 return registerProofIfNeeded(avalanche, std::move(proof), state);
79}
80
82 const std::string &dgHex, CPubKey &auth) {
86 }
87
89 if (!dg.verify(state, auth)) {
91 "The delegation is invalid: " + state.ToString());
92 }
93}
94
96 const std::string &proofHex) {
100 }
101
102 Amount stakeUtxoDustThreshold = avalanche::PROOF_DUST_THRESHOLD;
103 if (node.avalanche) {
104 // If Avalanche is enabled, use the configured dust threshold
105 node.avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
106 stakeUtxoDustThreshold = pm.getStakeUtxoDustThreshold();
107 });
108 }
109
111 {
112 LOCK(cs_main);
113 if (!proof.verify(stakeUtxoDustThreshold, *Assert(node.chainman),
114 state)) {
116 "The proof is invalid: " + state.ToString());
117 }
118 }
119}
120
122 return RPCHelpMan{
123 "addavalanchenode",
124 "Add a node in the set of peers to poll for avalanche.\n",
125 {
127 "Node to be added to avalanche."},
129 "The public key of the node."},
131 "Proof that the node is not a sybil."},
133 "The proof delegation the the node public key"},
134 },
136 "Whether the addition succeeded or not."},
138 HelpExampleRpc("addavalanchenode", "5, \"<pubkey>\", \"<proof>\"")},
139 [&](const RPCHelpMan &self, const Config &config,
140 const JSONRPCRequest &request) -> UniValue {
141 const NodeId nodeid = request.params[0].getInt<int64_t>();
142 CPubKey key = ParsePubKey(request.params[1]);
143
144 auto proof = RCUPtr<avalanche::Proof>::make();
145 NodeContext &node = EnsureAnyNodeContext(request.context);
147
148 verifyProofOrThrow(node, *proof, request.params[2].get_str());
149
150 const avalanche::ProofId &proofid = proof->getId();
151 if (key != proof->getMaster()) {
152 if (request.params.size() < 4 || request.params[3].isNull()) {
153 throw JSONRPCError(
155 "The public key does not match the proof");
156 }
157
160 verifyDelegationOrThrow(dg, request.params[3].get_str(), auth);
161
162 if (dg.getProofId() != proofid) {
163 throw JSONRPCError(
165 "The delegation does not match the proof");
166 }
167
168 if (key != auth) {
169 throw JSONRPCError(
171 "The public key does not match the delegation");
172 }
173 }
174
175 if (!registerProofIfNeeded(avalanche, proof)) {
177 "The proof has conflicting utxos");
178 }
179
180 if (!node.connman->ForNode(nodeid, [&](CNode *pnode) {
181 LOCK(pnode->cs_avalanche_pubkey);
182 bool expected = false;
183 if (pnode->m_avalanche_enabled.compare_exchange_strong(
184 expected, true)) {
185 pnode->m_avalanche_pubkey = std::move(key);
186 }
187 return true;
188 })) {
189 throw JSONRPCError(
191 strprintf("The node does not exist: %d", nodeid));
192 }
193
194 return avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
195 if (!pm.addNode(nodeid, proofid)) {
196 return false;
197 }
198
199 pm.addUnbroadcastProof(proofid);
200 return true;
201 });
202 },
203 };
204}
205
207 return RPCHelpMan{
208 "buildavalancheproof",
209 "Build a proof for avalanche's sybil resistance.\n",
210 {
212 "The proof's sequence"},
214 "A timestamp indicating when the proof expire"},
216 "The master private key in base58-encoding"},
217 {
218 "stakes",
221 "The stakes to be signed and associated private keys",
222 {
223 {
224 "stake",
227 "A stake to be attached to this proof",
228 {
229 {"txid", RPCArg::Type::STR_HEX,
230 RPCArg::Optional::NO, "The transaction id"},
232 "The output number"},
233 {"amount", RPCArg::Type::AMOUNT,
234 RPCArg::Optional::NO, "The amount in this UTXO"},
236 "The height at which this UTXO was mined"},
237 {"iscoinbase", RPCArg::Type::BOOL,
238 RPCArg::Default{false},
239 "Indicate wether the UTXO is a coinbase"},
240 {"privatekey", RPCArg::Type::STR,
242 "private key in base58-encoding"},
243 },
244 },
245 },
246 },
247 {"payoutAddress", RPCArg::Type::STR, RPCArg::Optional::NO,
248 "A payout address"},
249 },
251 "A string that is a serialized, hex-encoded proof data."},
252 RPCExamples{HelpExampleRpc("buildavalancheproof",
253 "0 1234567800 \"<master>\" []")},
254 [&](const RPCHelpMan &self, const Config &config,
255 const JSONRPCRequest &request) -> UniValue {
256 const uint64_t sequence = request.params[0].getInt<int64_t>();
257 const int64_t expiration = request.params[1].getInt<int64_t>();
258
259 CKey masterKey = DecodeSecret(request.params[2].get_str());
260 if (!masterKey.IsValid()) {
261 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid master key");
262 }
263
265 request.params[4].get_str(), config.GetChainParams());
266
269 "Invalid payout address");
270 }
271
272 avalanche::ProofBuilder pb(sequence, expiration, masterKey,
274
275 const UniValue &stakes = request.params[3].get_array();
276 for (size_t i = 0; i < stakes.size(); i++) {
277 const UniValue &stake = stakes[i];
279 stake,
280 {
281 {"txid", UniValue::VSTR},
282 {"vout", UniValue::VNUM},
283 // "amount" is also required but check is done below
284 // due to UniValue::VNUM erroneously not accepting
285 // quoted numerics (which are valid JSON)
286 {"height", UniValue::VNUM},
287 {"privatekey", UniValue::VSTR},
288 });
289
290 int nOut = stake.find_value("vout").getInt<int>();
291 if (nOut < 0) {
293 "vout cannot be negative");
294 }
295
296 const int height = stake.find_value("height").getInt<int>();
297 if (height < 1) {
299 "height must be positive");
300 }
301
302 const TxId txid(ParseHashO(stake, "txid"));
303 const COutPoint utxo(txid, nOut);
304
305 if (!stake.exists("amount")) {
306 throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount");
307 }
308
309 const Amount amount =
310 AmountFromValue(stake.find_value("amount"));
311
312 const UniValue &iscbparam = stake.find_value("iscoinbase");
313 const bool iscoinbase =
314 iscbparam.isNull() ? false : iscbparam.get_bool();
315 CKey key =
316 DecodeSecret(stake.find_value("privatekey").get_str());
317
318 if (!key.IsValid()) {
320 "Invalid private key");
321 }
322
323 if (!pb.addUTXO(utxo, amount, uint32_t(height), iscoinbase,
324 std::move(key))) {
326 "Duplicated stake");
327 }
328 }
329
330 const avalanche::ProofRef proof = pb.build();
331
332 return proof->ToHex();
333 },
334 };
335}
336
338 return RPCHelpMan{
339 "decodeavalancheproof",
340 "Convert a serialized, hex-encoded proof, into JSON object. "
341 "The validity of the proof is not verified.\n",
342 {
344 "The proof hex string"},
345 },
346 RPCResult{
348 "",
349 "",
350 {
351 {RPCResult::Type::NUM, "sequence",
352 "The proof's sequential number"},
353 {RPCResult::Type::NUM, "expiration",
354 "A timestamp indicating when the proof expires"},
355 {RPCResult::Type::STR_HEX, "master", "The master public key"},
356 {RPCResult::Type::STR, "signature",
357 "The proof signature (base64 encoded)"},
359 "payoutscript",
360 "The proof payout script",
361 {
362 {RPCResult::Type::STR, "asm", "Decoded payout script"},
364 "Raw payout script in hex format"},
365 {RPCResult::Type::STR, "type",
366 "The output type (e.g. " + GetAllOutputTypes() + ")"},
367 {RPCResult::Type::NUM, "reqSigs",
368 "The required signatures"},
370 "addresses",
371 "",
372 {
373 {RPCResult::Type::STR, "address", "eCash address"},
374 }},
375 }},
376 {RPCResult::Type::STR_HEX, "limitedid",
377 "A hash of the proof data excluding the master key."},
378 {RPCResult::Type::STR_HEX, "proofid",
379 "A hash of the limitedid and master key."},
380 {RPCResult::Type::STR_AMOUNT, "staked_amount",
381 "The total staked amount of this proof in " +
382 Currency::get().ticker + "."},
383 {RPCResult::Type::NUM, "score", "The score of this proof."},
385 "stakes",
386 "",
387 {
389 "",
390 "",
391 {
393 "The transaction id"},
394 {RPCResult::Type::NUM, "vout", "The output number"},
396 "The amount in this UTXO"},
397 {RPCResult::Type::NUM, "height",
398 "The height at which this UTXO was mined"},
399 {RPCResult::Type::BOOL, "iscoinbase",
400 "Indicate whether the UTXO is a coinbase"},
401 {RPCResult::Type::STR_HEX, "pubkey",
402 "This UTXO's public key"},
403 {RPCResult::Type::STR, "signature",
404 "Signature of the proofid with this UTXO's private "
405 "key (base64 encoded)"},
406 }},
407 }},
408 }},
409 RPCExamples{HelpExampleCli("decodeavalancheproof", "\"<hex proof>\"") +
410 HelpExampleRpc("decodeavalancheproof", "\"<hex proof>\"")},
411 [&](const RPCHelpMan &self, const Config &config,
412 const JSONRPCRequest &request) -> UniValue {
413 avalanche::Proof proof;
415 if (!avalanche::Proof::FromHex(proof, request.params[0].get_str(),
416 error)) {
418 }
419
420 UniValue result(UniValue::VOBJ);
421 result.pushKV("sequence", proof.getSequence());
422 result.pushKV("expiration", proof.getExpirationTime());
423 result.pushKV("master", HexStr(proof.getMaster()));
424 result.pushKV("signature", EncodeBase64(proof.getSignature()));
425
426 const auto payoutScript = proof.getPayoutScript();
429 /* fIncludeHex */ true);
430 result.pushKV("payoutscript", payoutScriptObj);
431
432 result.pushKV("limitedid", proof.getLimitedId().ToString());
433 result.pushKV("proofid", proof.getId().ToString());
434
435 result.pushKV("staked_amount", proof.getStakedAmount());
436 result.pushKV("score", uint64_t(proof.getScore()));
437
438 UniValue stakes(UniValue::VARR);
439 for (const avalanche::SignedStake &s : proof.getStakes()) {
440 const COutPoint &utxo = s.getStake().getUTXO();
442 stake.pushKV("txid", utxo.GetTxId().ToString());
443 stake.pushKV("vout", uint64_t(utxo.GetN()));
444 stake.pushKV("amount", s.getStake().getAmount());
445 stake.pushKV("height", uint64_t(s.getStake().getHeight()));
446 stake.pushKV("iscoinbase", s.getStake().isCoinbase());
447 stake.pushKV("pubkey", HexStr(s.getStake().getPubkey()));
448 // Only PKHash destination is supported, so this is safe
449 stake.pushKV("address",
450 EncodeDestination(PKHash(s.getStake().getPubkey()),
451 config));
452 stake.pushKV("signature", EncodeBase64(s.getSignature()));
453 stakes.push_back(stake);
454 }
455 result.pushKV("stakes", stakes);
456
457 return result;
458 },
459 };
460}
461
463 return RPCHelpMan{
464 "delegateavalancheproof",
465 "Delegate the avalanche proof to another public key.\n",
466 {
468 "The limited id of the proof to be delegated."},
470 "The private key in base58-encoding. Must match the proof master "
471 "public key or the upper level parent delegation public key if "
472 " supplied."},
474 "The public key to delegate the proof to."},
476 "A string that is the serialized, hex-encoded delegation for the "
477 "proof and which is a parent for the delegation to build."},
478 },
480 "A string that is a serialized, hex-encoded delegation."},
482 HelpExampleRpc("delegateavalancheproof",
483 "\"<limitedproofid>\" \"<privkey>\" \"<pubkey>\"")},
484 [&](const RPCHelpMan &self, const Config &config,
485 const JSONRPCRequest &request) -> UniValue {
486 avalanche::LimitedProofId limitedProofId{
487 ParseHashV(request.params[0], "limitedproofid")};
488
489 const CKey privkey = DecodeSecret(request.params[1].get_str());
490 if (!privkey.IsValid()) {
492 "The private key is invalid");
493 }
494
495 const CPubKey pubkey = ParsePubKey(request.params[2]);
496
497 std::unique_ptr<avalanche::DelegationBuilder> dgb;
498 if (request.params.size() >= 4 && !request.params[3].isNull()) {
501 verifyDelegationOrThrow(dg, request.params[3].get_str(), auth);
502
503 if (dg.getProofId() !=
504 limitedProofId.computeProofId(dg.getProofMaster())) {
505 throw JSONRPCError(
507 "The delegation does not match the proof");
508 }
509
510 if (privkey.GetPubKey() != auth) {
511 throw JSONRPCError(
513 "The private key does not match the delegation");
514 }
515
516 dgb = std::make_unique<avalanche::DelegationBuilder>(dg);
517 } else {
518 dgb = std::make_unique<avalanche::DelegationBuilder>(
519 limitedProofId, privkey.GetPubKey());
520 }
521
522 if (!dgb->addLevel(privkey, pubkey)) {
524 "Unable to build the delegation");
525 }
526
528 ss << dgb->build();
529 return HexStr(ss);
530 },
531 };
532}
533
535 return RPCHelpMan{
536 "decodeavalanchedelegation",
537 "Convert a serialized, hex-encoded avalanche proof delegation, into "
538 "JSON object. \n"
539 "The validity of the delegation is not verified.\n",
540 {
542 "The delegation hex string"},
543 },
544 RPCResult{
546 "",
547 "",
548 {
549 {RPCResult::Type::STR_HEX, "pubkey",
550 "The public key the proof is delegated to."},
551 {RPCResult::Type::STR_HEX, "proofmaster",
552 "The delegated proof master public key."},
553 {RPCResult::Type::STR_HEX, "delegationid",
554 "The identifier of this delegation."},
555 {RPCResult::Type::STR_HEX, "limitedid",
556 "A delegated proof data hash excluding the master key."},
557 {RPCResult::Type::STR_HEX, "proofid",
558 "A hash of the delegated proof limitedid and master key."},
559 {RPCResult::Type::NUM, "depth",
560 "The number of delegation levels."},
562 "levels",
563 "",
564 {
566 "",
567 "",
568 {
569 {RPCResult::Type::NUM, "index",
570 "The index of this delegation level."},
571 {RPCResult::Type::STR_HEX, "pubkey",
572 "This delegated public key for this level"},
573 {RPCResult::Type::STR, "signature",
574 "Signature of this delegation level (base64 "
575 "encoded)"},
576 }},
577 }},
578 }},
579 RPCExamples{HelpExampleCli("decodeavalanchedelegation",
580 "\"<hex delegation>\"") +
581 HelpExampleRpc("decodeavalanchedelegation",
582 "\"<hex delegation>\"")},
583 [&](const RPCHelpMan &self, const Config &config,
584 const JSONRPCRequest &request) -> UniValue {
585 avalanche::Delegation delegation;
588 delegation, request.params[0].get_str(), error)) {
590 }
591
592 UniValue result(UniValue::VOBJ);
593 result.pushKV("pubkey", HexStr(delegation.getDelegatedPubkey()));
594 result.pushKV("proofmaster", HexStr(delegation.getProofMaster()));
595 result.pushKV("delegationid", delegation.getId().ToString());
596 result.pushKV("limitedid",
597 delegation.getLimitedProofId().ToString());
598 result.pushKV("proofid", delegation.getProofId().ToString());
599
600 auto levels = delegation.getLevels();
601 result.pushKV("depth", uint64_t(levels.size()));
602
604 for (auto &level : levels) {
606 obj.pushKV("pubkey", HexStr(level.pubkey));
607 obj.pushKV("signature", EncodeBase64(level.sig));
608 levelsArray.push_back(std::move(obj));
609 }
610 result.pushKV("levels", levelsArray);
611
612 return result;
613 },
614 };
615}
616
618 return RPCHelpMan{
619 "getavalancheinfo",
620 "Returns an object containing various state info regarding avalanche "
621 "networking.\n",
622 {},
623 RPCResult{
625 "",
626 "",
627 {
628 {RPCResult::Type::BOOL, "ready_to_poll",
629 "Whether the node is ready to start polling and voting."},
631 "local",
632 "Only available if -avaproof has been supplied to the node",
633 {
634 {RPCResult::Type::BOOL, "verified",
635 "Whether the node local proof has been locally verified "
636 "or not."},
637 {RPCResult::Type::STR, "verification_status",
638 "The proof verification status. Only available if the "
639 "\"verified\" flag is false."},
640 {RPCResult::Type::STR_HEX, "proofid",
641 "The node local proof id."},
642 {RPCResult::Type::STR_HEX, "limited_proofid",
643 "The node local limited proof id."},
644 {RPCResult::Type::STR_HEX, "master",
645 "The node local proof master public key."},
646 {RPCResult::Type::STR, "payout_address",
647 "The node local proof payout address. This might be "
648 "omitted if the payout script is not one of P2PK, P2PKH "
649 "or P2SH, in which case decodeavalancheproof can be used "
650 "to get more details."},
651 {RPCResult::Type::STR_AMOUNT, "stake_amount",
652 "The node local proof staked amount."},
653 }},
655 "network",
656 "",
657 {
658 {RPCResult::Type::NUM, "proof_count",
659 "The number of valid avalanche proofs we know exist "
660 "(including this node's local proof if applicable)."},
661 {RPCResult::Type::NUM, "connected_proof_count",
662 "The number of avalanche proofs with at least one node "
663 "we are connected to (including this node's local proof "
664 "if applicable)."},
665 {RPCResult::Type::NUM, "dangling_proof_count",
666 "The number of avalanche proofs with no node attached."},
667 {RPCResult::Type::NUM, "finalized_proof_count",
668 "The number of known avalanche proofs that have been "
669 "finalized by avalanche."},
670 {RPCResult::Type::NUM, "conflicting_proof_count",
671 "The number of known avalanche proofs that conflict with "
672 "valid proofs."},
673 {RPCResult::Type::NUM, "immature_proof_count",
674 "The number of known avalanche proofs that have immature "
675 "utxos."},
676 {RPCResult::Type::STR_AMOUNT, "total_stake_amount",
677 "The total staked amount over all the valid proofs in " +
679 " (including this node's local proof if "
680 "applicable)."},
681 {RPCResult::Type::STR_AMOUNT, "connected_stake_amount",
682 "The total staked amount over all the connected proofs "
683 "in " +
685 " (including this node's local proof if "
686 "applicable)."},
687 {RPCResult::Type::STR_AMOUNT, "dangling_stake_amount",
688 "The total staked amount over all the dangling proofs "
689 "in " +
691 " (including this node's local proof if "
692 "applicable)."},
693 {RPCResult::Type::STR_AMOUNT, "immature_stake_amount",
694 "The total staked amount over all the immature proofs "
695 "in " +
697 " (including this node's local proof if "
698 "applicable)."},
699 {RPCResult::Type::NUM, "node_count",
700 "The number of avalanche nodes we are connected to "
701 "(including this node if a local proof is set)."},
702 {RPCResult::Type::NUM, "connected_node_count",
703 "The number of avalanche nodes associated with an "
704 "avalanche proof (including this node if a local proof "
705 "is set)."},
706 {RPCResult::Type::NUM, "pending_node_count",
707 "The number of avalanche nodes pending for a proof."},
708 }},
709 },
710 },
711 RPCExamples{HelpExampleCli("getavalancheinfo", "") +
712 HelpExampleRpc("getavalancheinfo", "")},
713 [&](const RPCHelpMan &self, const Config &config,
714 const JSONRPCRequest &request) -> UniValue {
715 NodeContext &node = EnsureAnyNodeContext(request.context);
717
719 ret.pushKV("ready_to_poll", avalanche.isQuorumEstablished());
720
721 auto localProof = avalanche.getLocalProof();
722 if (localProof != nullptr) {
724 const bool verified = avalanche.withPeerManager(
725 [&](const avalanche::PeerManager &pm) {
726 const avalanche::ProofId &proofid = localProof->getId();
727 return pm.isBoundToPeer(proofid);
728 });
729 local.pushKV("verified", verified);
730 const bool sharing = avalanche.canShareLocalProof();
731 if (!verified) {
733 avalanche.getLocalProofRegistrationState();
734 // If the local proof is not registered but the state is
735 // valid, no registration attempt occurred yet.
736 local.pushKV("verification_status",
737 state.IsValid()
738 ? (sharing ? "pending verification"
739 : "pending inbound connections")
740 : state.GetRejectReason());
741 }
742 local.pushKV("proofid", localProof->getId().ToString());
743 local.pushKV("limited_proofid",
744 localProof->getLimitedId().ToString());
745 local.pushKV("master", HexStr(localProof->getMaster()));
746 CTxDestination destination;
747 if (ExtractDestination(localProof->getPayoutScript(),
748 destination)) {
749 local.pushKV("payout_address",
750 EncodeDestination(destination, config));
751 }
752 local.pushKV("stake_amount", localProof->getStakedAmount());
753 ret.pushKV("local", local);
754 }
755
756 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
757 UniValue network(UniValue::VOBJ);
758
765
766 pm.forEachPeer([&](const avalanche::Peer &peer) {
767 CHECK_NONFATAL(peer.proof != nullptr);
768
769 const bool isLocalProof =
770 localProof &&
771 peer.proof->getId() == localProof->getId();
772
773 ++proofCount;
774 const Amount proofStake = peer.proof->getStakedAmount();
775
777
778 if (peer.hasFinalized) {
780 }
781
782 if (peer.node_count > 0 || isLocalProof) {
785 }
786
788 });
789
791 pm.getImmatureProofPool().forEachProof(
792 [&](const avalanche::ProofRef &proof) {
793 immatureStakes += proof->getStakedAmount();
794 });
795
796 network.pushKV("proof_count", proofCount);
797 network.pushKV("connected_proof_count", connectedProofCount);
798 network.pushKV("dangling_proof_count",
800
801 network.pushKV("finalized_proof_count", finalizedProofCount);
802 network.pushKV(
803 "conflicting_proof_count",
804 uint64_t(pm.getConflictingProofPool().countProofs()));
805 network.pushKV(
806 "immature_proof_count",
807 uint64_t(pm.getImmatureProofPool().countProofs()));
808
809 network.pushKV("total_stake_amount", totalStakes);
810 network.pushKV("connected_stake_amount", connectedStakes);
811 network.pushKV("dangling_stake_amount",
813 network.pushKV("immature_stake_amount", immatureStakes);
814
815 const uint64_t pendingNodes = pm.getPendingNodeCount();
816 network.pushKV("node_count", connectedNodeCount + pendingNodes);
817 network.pushKV("connected_node_count", connectedNodeCount);
818 network.pushKV("pending_node_count", pendingNodes);
819
820 ret.pushKV("network", network);
821 });
822
823 return ret;
824 },
825 };
826}
827
829 return RPCHelpMan{
830 "getavalanchepeerinfo",
831 "Returns data about an avalanche peer as a json array of objects. If "
832 "no proofid is provided, returns data about all the peers.\n",
833 {
835 "The hex encoded avalanche proof identifier."},
836 },
837 RPCResult{
839 "",
840 "",
841 {{
843 "",
844 "",
845 {{
846 {RPCResult::Type::NUM, "avalanche_peerid",
847 "The avalanche internal peer identifier"},
848 {RPCResult::Type::NUM, "availability_score",
849 "The agreggated availability score of this peer's nodes"},
850 {RPCResult::Type::STR_HEX, "proofid",
851 "The avalanche proof id used by this peer"},
852 {RPCResult::Type::STR_HEX, "proof",
853 "The avalanche proof used by this peer"},
854 {RPCResult::Type::NUM, "nodecount",
855 "The number of nodes for this peer"},
857 "node_list",
858 "",
859 {
860 {RPCResult::Type::NUM, "nodeid",
861 "Node id, as returned by getpeerinfo"},
862 }},
863 }},
864 }},
865 },
866 RPCExamples{HelpExampleCli("getavalanchepeerinfo", "") +
867 HelpExampleCli("getavalanchepeerinfo", "\"proofid\"") +
868 HelpExampleRpc("getavalanchepeerinfo", "") +
869 HelpExampleRpc("getavalanchepeerinfo", "\"proofid\"")},
870 [&](const RPCHelpMan &self, const Config &config,
871 const JSONRPCRequest &request) -> UniValue {
872 NodeContext &node = EnsureAnyNodeContext(request.context);
874
875 auto peerToUniv = [](const avalanche::PeerManager &pm,
876 const avalanche::Peer &peer) {
878
879 obj.pushKV("avalanche_peerid", uint64_t(peer.peerid));
880 obj.pushKV("availability_score", peer.availabilityScore);
881 obj.pushKV("proofid", peer.getProofId().ToString());
882 obj.pushKV("proof", peer.proof->ToHex());
883
885 pm.forEachNode(peer, [&](const avalanche::Node &n) {
886 nodes.push_back(n.nodeid);
887 });
888
889 obj.pushKV("nodecount", uint64_t(peer.node_count));
890 obj.pushKV("node_list", nodes);
891
892 return obj;
893 };
894
896
897 avalanche.withPeerManager([&](const avalanche::PeerManager &pm) {
898 // If a proofid is provided, only return the associated peer
899 if (!request.params[0].isNull()) {
900 const avalanche::ProofId proofid =
901 avalanche::ProofId::fromHex(
902 request.params[0].get_str());
903 if (!pm.isBoundToPeer(proofid)) {
904 throw JSONRPCError(RPC_INVALID_PARAMETER,
905 "Proofid not found");
906 }
907
908 pm.forPeer(proofid, [&](const avalanche::Peer &peer) {
909 ret.push_back(peerToUniv(pm, peer));
910 return true;
911 });
912
913 return;
914 }
915
916 // If no proofid is provided, return all the peers
917 pm.forEachPeer([&](const avalanche::Peer &peer) {
918 ret.push_back(peerToUniv(pm, peer));
919 });
920 });
921
922 return ret;
923 },
924 };
925}
926
928 return RPCHelpMan{
929 "getavalancheproofs",
930 "Returns an object containing all tracked proofids.\n",
931 {},
932 RPCResult{
934 "",
935 "",
936 {
938 "valid",
939 "",
940 {
941 {RPCResult::Type::STR_HEX, "proofid",
942 "Avalanche proof id"},
943 }},
945 "conflicting",
946 "",
947 {
948 {RPCResult::Type::STR_HEX, "proofid",
949 "Avalanche proof id"},
950 }},
952 "immature",
953 "",
954 {
955 {RPCResult::Type::STR_HEX, "proofid",
956 "Avalanche proof id"},
957 }},
958 },
959 },
960 RPCExamples{HelpExampleCli("getavalancheproofs", "") +
961 HelpExampleRpc("getavalancheproofs", "")},
962 [&](const RPCHelpMan &self, const Config &config,
963 const JSONRPCRequest &request) -> UniValue {
964 NodeContext &node = EnsureAnyNodeContext(request.context);
966
968 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
969 auto appendProofIds = [&ret](const avalanche::ProofPool &pool,
970 const std::string &key) {
972 for (const avalanche::ProofId &proofid :
973 pool.getProofIds()) {
974 arrOut.push_back(proofid.ToString());
975 }
976 ret.pushKV(key, arrOut);
977 };
978
979 appendProofIds(pm.getValidProofPool(), "valid");
980 appendProofIds(pm.getConflictingProofPool(), "conflicting");
981 appendProofIds(pm.getImmatureProofPool(), "immature");
982 });
983
984 return ret;
985 },
986 };
987}
988
990 return RPCHelpMan{
991 "getstakingreward",
992 "Return a list of possible staking reward winners based on the "
993 "previous "
994 "block hash.\n",
995 {
997 "The previous block hash, hex encoded."},
998 {"recompute", RPCArg::Type::BOOL, RPCArg::Default{false},
999 "Whether to recompute the staking reward winner if there is a "
1000 "cached value."},
1001 },
1002 RPCResult{
1004 "",
1005 "",
1006 {
1008 "winner",
1009 "The winning proof",
1010 {
1011 {RPCResult::Type::STR_HEX, "proofid",
1012 "The winning proofid"},
1013 {RPCResult::Type::STR, "asm", "Decoded payout script"},
1015 "Raw payout script in hex format"},
1016 {RPCResult::Type::STR, "type",
1017 "The output type (e.g. " + GetAllOutputTypes() + ")"},
1018 {RPCResult::Type::NUM, "reqSigs",
1019 "The required signatures"},
1021 "addresses",
1022 "",
1023 {
1024 {RPCResult::Type::STR, "address", "eCash address"},
1025 }},
1026 }},
1027 }},
1028 RPCExamples{HelpExampleRpc("getstakingreward", "<blockhash>")},
1029 [&](const RPCHelpMan &self, const Config &config,
1030 const JSONRPCRequest &request) -> UniValue {
1031 const NodeContext &node = EnsureAnyNodeContext(request.context);
1034
1035 const BlockHash blockhash(
1036 ParseHashV(request.params[0], "blockhash"));
1037
1038 const CBlockIndex *pprev;
1039 {
1040 LOCK(cs_main);
1041 pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1042 }
1043
1044 if (!pprev) {
1045 throw JSONRPCError(
1047 strprintf("Block not found: %s\n", blockhash.ToString()));
1048 }
1049
1051 config.GetChainParams().GetConsensus(), pprev)) {
1052 throw JSONRPCError(
1054 strprintf(
1055 "Staking rewards are not activated for block %s\n",
1056 blockhash.ToString()));
1057 }
1058
1059 if (!request.params[1].isNull() && request.params[1].get_bool()) {
1060 // Force recompute the staking reward winner by first erasing
1061 // the cached entry if any
1062 avalanche.eraseStakingRewardWinner(blockhash);
1063 }
1064
1065 if (!avalanche.computeStakingReward(pprev)) {
1066 throw JSONRPCError(
1068 strprintf("Unable to determine a staking reward winner "
1069 "for block %s\n",
1070 blockhash.ToString()));
1071 }
1072
1073 std::vector<std::pair<avalanche::ProofId, CScript>> winners;
1074 if (!avalanche.getStakingRewardWinners(blockhash, winners)) {
1075 throw JSONRPCError(
1077 strprintf("Unable to retrieve the staking reward winner "
1078 "for block %s\n",
1079 blockhash.ToString()));
1080 }
1081
1083 for (auto &winner : winners) {
1086 /*fIncludeHex=*/true);
1087 stakingRewardsObj.pushKV("proofid", winner.first.GetHex());
1088 winnersArr.push_back(stakingRewardsObj);
1089 }
1090
1091 return winnersArr;
1092 },
1093 };
1094}
1095
1097 return RPCHelpMan{
1098 "hasstakingreward",
1099 "Return true if a staking reward winner exists based on the previous "
1100 "block hash.\n",
1101 {
1103 "The previous block hash, hex encoded."},
1104 },
1106 "Whether staking reward winner has been computed for "
1107 "previous block hash or not."},
1108 RPCExamples{HelpExampleRpc("hasstakingreward", "<blockhash>")},
1109 [&](const RPCHelpMan &self, const Config &config,
1110 const JSONRPCRequest &request) -> UniValue {
1111 const NodeContext &node = EnsureAnyNodeContext(request.context);
1114
1115 const BlockHash blockhash(
1116 ParseHashV(request.params[0], "blockhash"));
1117
1118 const CBlockIndex *pprev;
1119 {
1120 LOCK(cs_main);
1121 pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1122 }
1123
1124 if (!pprev) {
1125 throw JSONRPCError(
1127 strprintf("Block not found: %s\n", blockhash.ToString()));
1128 }
1129
1131 config.GetChainParams().GetConsensus(), pprev)) {
1132 throw JSONRPCError(
1134 strprintf(
1135 "Staking rewards are not activated for block %s\n",
1136 blockhash.ToString()));
1137 }
1138
1139 std::vector<std::pair<avalanche::ProofId, CScript>> winners;
1140 if (!avalanche.getStakingRewardWinners(blockhash, winners)) {
1141 return false;
1142 }
1143 return winners.size() > 0;
1144 },
1145 };
1146}
1147
1149 return RPCHelpMan{
1150 "setstakingreward",
1151 "Set the staking reward winner for the given previous block hash.\n",
1152 {
1154 "The previous block hash, hex encoded."},
1156 "The payout script for the staking reward, hex encoded."},
1157 {"append", RPCArg::Type::BOOL, RPCArg::Default{false},
1158 "Append to the list of possible winners instead of replacing."},
1159 },
1161 "Whether the payout script was set or not"},
1163 HelpExampleRpc("setstakingreward", "<blockhash> <payout script>")},
1164 [&](const RPCHelpMan &self, const Config &config,
1165 const JSONRPCRequest &request) -> UniValue {
1166 const NodeContext &node = EnsureAnyNodeContext(request.context);
1169
1170 const BlockHash blockhash(
1171 ParseHashV(request.params[0], "blockhash"));
1172
1173 const CBlockIndex *pprev;
1174 {
1175 LOCK(cs_main);
1176 pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1177 }
1178
1179 if (!pprev) {
1180 throw JSONRPCError(
1182 strprintf("Block not found: %s\n", blockhash.ToString()));
1183 }
1184
1186 config.GetChainParams().GetConsensus(), pprev)) {
1187 throw JSONRPCError(
1189 strprintf(
1190 "Staking rewards are not activated for block %s\n",
1191 blockhash.ToString()));
1192 }
1193
1194 const std::vector<uint8_t> data =
1195 ParseHex(request.params[1].get_str());
1196 CScript payoutScript(data.begin(), data.end());
1197
1198 std::vector<CScript> payoutScripts;
1199
1200 if (!request.params[2].isNull() && request.params[2].get_bool()) {
1201 // Append mode, initialize our list with the current winners
1202 // and the new one will be added to the back of that list. If
1203 // there is no winner the list will remain empty.
1204 avalanche.getStakingRewardWinners(blockhash, payoutScripts);
1205 }
1206
1207 if (std::find(payoutScripts.begin(), payoutScripts.end(),
1208 payoutScript) != payoutScripts.end()) {
1209 throw JSONRPCError(
1211 strprintf(
1212 "Staking rewards winner is already set for block %s\n",
1213 blockhash.ToString()));
1214 }
1215
1216 payoutScripts.push_back(std::move(payoutScript));
1217
1218 // This will return true upon insertion or false upon replacement.
1219 // We want to convey the success of the RPC, so we always return
1220 // true.
1221 avalanche.setStakingRewardWinners(pprev, payoutScripts);
1222 return true;
1223 },
1224 };
1225}
1226
1228 return RPCHelpMan{
1229 "getremoteproofs",
1230 "Get the list of remote proofs for the given node id.\n",
1231 {
1233 "The node identifier."},
1234 },
1235 RPCResult{
1237 "proofs",
1238 "",
1239 {{
1241 "proof",
1242 "",
1243 {{
1244 {RPCResult::Type::STR_HEX, "proofid",
1245 "The hex encoded proof identifier."},
1246 {RPCResult::Type::BOOL, "present",
1247 "Whether the node has the proof."},
1248 {RPCResult::Type::NUM, "last_update",
1249 "The last time this proof status was updated."},
1250 }},
1251 }},
1252 },
1253 RPCExamples{HelpExampleRpc("getremoteproofs", "<nodeid>")},
1254 [&](const RPCHelpMan &self, const Config &config,
1255 const JSONRPCRequest &request) -> UniValue {
1256 NodeContext &node = EnsureAnyNodeContext(request.context);
1258
1259 const NodeId nodeid = request.params[0].getInt<int64_t>();
1260 auto remoteProofs = avalanche.withPeerManager(
1261 [nodeid](const avalanche::PeerManager &pm) {
1262 return pm.getRemoteProofs(nodeid);
1263 });
1264
1266
1267 for (const auto &remoteProof : remoteProofs) {
1269 obj.pushKV("proofid", remoteProof.proofid.ToString());
1270 obj.pushKV("present", remoteProof.present);
1271 obj.pushKV("last_update", remoteProof.lastUpdate.count());
1272
1273 arrOut.push_back(obj);
1274 }
1275
1276 return arrOut;
1277 },
1278 };
1279}
1280
1282 return RPCHelpMan{
1283 "getrawavalancheproof",
1284 "Lookup for a known avalanche proof by id.\n",
1285 {
1287 "The hex encoded avalanche proof identifier."},
1288 },
1289 RPCResult{
1291 "",
1292 "",
1293 {{
1294 {RPCResult::Type::STR_HEX, "proof",
1295 "The hex encoded proof matching the identifier."},
1296 {RPCResult::Type::BOOL, "immature",
1297 "Whether the proof has immature utxos."},
1298 {RPCResult::Type::BOOL, "boundToPeer",
1299 "Whether the proof is bound to an avalanche peer."},
1300 {RPCResult::Type::BOOL, "conflicting",
1301 "Whether the proof has a conflicting UTXO with an avalanche "
1302 "peer."},
1303 {RPCResult::Type::BOOL, "finalized",
1304 "Whether the proof is finalized by vote."},
1305 }},
1306 },
1307 RPCExamples{HelpExampleRpc("getrawavalancheproof", "<proofid>")},
1308 [&](const RPCHelpMan &self, const Config &config,
1309 const JSONRPCRequest &request) -> UniValue {
1310 NodeContext &node = EnsureAnyNodeContext(request.context);
1312
1313 const avalanche::ProofId proofid =
1314 avalanche::ProofId::fromHex(request.params[0].get_str());
1315
1316 bool isImmature = false;
1317 bool isBoundToPeer = false;
1318 bool conflicting = false;
1319 bool finalized = false;
1320 auto proof = avalanche.withPeerManager(
1321 [&](const avalanche::PeerManager &pm) {
1322 isImmature = pm.isImmature(proofid);
1323 isBoundToPeer = pm.isBoundToPeer(proofid);
1324 conflicting = pm.isInConflictingPool(proofid);
1325 finalized =
1326 pm.forPeer(proofid, [&](const avalanche::Peer &p) {
1327 return p.hasFinalized;
1328 });
1329 return pm.getProof(proofid);
1330 });
1331
1332 if (!proof) {
1333 throw JSONRPCError(RPC_INVALID_PARAMETER, "Proof not found");
1334 }
1335
1337
1339 ss << *proof;
1340 ret.pushKV("proof", HexStr(ss));
1341 ret.pushKV("immature", isImmature);
1342 ret.pushKV("boundToPeer", isBoundToPeer);
1343 ret.pushKV("conflicting", conflicting);
1344 ret.pushKV("finalized", finalized);
1345
1346 return ret;
1347 },
1348 };
1349}
1350
1352 return RPCHelpMan{
1353 "invalidateavalancheproof",
1354 "Reject a known avalanche proof by id.\n",
1355 {
1357 "The hex encoded avalanche proof identifier."},
1358 },
1359 RPCResult{
1361 "success",
1362 "",
1363 },
1364 RPCExamples{HelpExampleRpc("invalidateavalancheproof", "<proofid>")},
1365 [&](const RPCHelpMan &self, const Config &config,
1366 const JSONRPCRequest &request) -> UniValue {
1367 NodeContext &node = EnsureAnyNodeContext(request.context);
1369
1370 const avalanche::ProofId proofid =
1371 avalanche::ProofId::fromHex(request.params[0].get_str());
1372
1373 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
1374 if (!pm.exists(proofid) && !pm.isDangling(proofid)) {
1375 throw JSONRPCError(RPC_INVALID_PARAMETER,
1376 "Proof not found");
1377 }
1378
1379 if (!pm.rejectProof(
1380 proofid,
1382 throw JSONRPCError(RPC_INTERNAL_ERROR,
1383 "Failed to reject the proof");
1384 }
1385
1386 pm.setInvalid(proofid);
1387 });
1388
1389 if (avalanche.isRecentlyFinalized(proofid)) {
1390 // If the proof was previously finalized, clear the status.
1391 // Because there is no way to selectively delete an entry from a
1392 // Bloom filter, we have to clear the whole filter which could
1393 // cause extra voting rounds.
1394 avalanche.clearFinalizedItems();
1395 }
1396
1397 return true;
1398 },
1399 };
1400}
1401
1403 return RPCHelpMan{
1404 "isfinalblock",
1405 "Check if a block has been finalized by avalanche votes.\n",
1406 {
1408 "The hash of the block."},
1409 },
1411 "Whether the block has been finalized by avalanche votes."},
1412 RPCExamples{HelpExampleRpc("isfinalblock", "<block hash>") +
1413 HelpExampleCli("isfinalblock", "<block hash>")},
1414 [&](const RPCHelpMan &self, const Config &config,
1415 const JSONRPCRequest &request) -> UniValue {
1416 NodeContext &node = EnsureAnyNodeContext(request.context);
1418
1419 if (!avalanche.isQuorumEstablished()) {
1421 "Avalanche is not ready to poll yet.");
1422 }
1423
1424 ChainstateManager &chainman = EnsureAnyChainman(request.context);
1425 const BlockHash blockhash(
1426 ParseHashV(request.params[0], "blockhash"));
1427 const CBlockIndex *pindex;
1428
1429 {
1430 LOCK(cs_main);
1431 pindex = chainman.m_blockman.LookupBlockIndex(blockhash);
1432
1433 if (!pindex) {
1435 "Block not found");
1436 }
1437 }
1438
1439 return chainman.ActiveChainstate().IsBlockAvalancheFinalized(
1440 pindex);
1441 },
1442 };
1443}
1444
1446 return RPCHelpMan{
1447 "isfinaltransaction",
1448 "Check if a transaction has been finalized by avalanche votes.\n",
1449 {
1451 "The id of the transaction."},
1453 "The block in which to look for the transaction"},
1454 },
1455 RPCResult{
1456 RPCResult::Type::BOOL, "success",
1457 "Whether the transaction has been finalized by avalanche votes."},
1458 RPCExamples{HelpExampleRpc("isfinaltransaction", "<txid> <blockhash>") +
1459 HelpExampleCli("isfinaltransaction", "<txid> <blockhash>")},
1460 [&](const RPCHelpMan &self, const Config &config,
1461 const JSONRPCRequest &request) -> UniValue {
1462 const NodeContext &node = EnsureAnyNodeContext(request.context);
1464 const CTxMemPool &mempool = EnsureMemPool(node);
1466
1467 const TxId txid = TxId(ParseHashV(request.params[0], "txid"));
1468 CBlockIndex *pindex = nullptr;
1469
1470 if (!request.params[1].isNull()) {
1471 const BlockHash blockhash(
1472 ParseHashV(request.params[1], "blockhash"));
1473
1474 LOCK(cs_main);
1475 pindex = chainman.m_blockman.LookupBlockIndex(blockhash);
1476 if (!pindex) {
1478 "Block not found");
1479 }
1480 }
1481
1482 bool f_txindex_ready = false;
1483 if (g_txindex && !pindex) {
1484 f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain();
1485 }
1486
1488 const CTransactionRef tx = GetTransaction(
1489 pindex, &mempool, txid, hash_block, chainman.m_blockman);
1490
1491 if (!avalanche.isQuorumEstablished()) {
1493 "Avalanche is not ready to poll yet.");
1494 }
1495
1496 if (!tx) {
1497 std::string errmsg;
1498 if (pindex) {
1499 if (WITH_LOCK(::cs_main,
1500 return !pindex->nStatus.hasData())) {
1502 "Block data not downloaded yet.");
1503 }
1504 errmsg = "No such transaction found in the provided block.";
1505 } else if (!g_txindex) {
1506 errmsg = "No such transaction. Use -txindex or provide a "
1507 "block hash to enable blockchain transaction "
1508 "queries.";
1509 } else if (!f_txindex_ready) {
1510 errmsg = "No such transaction. Blockchain transactions are "
1511 "still in the process of being indexed.";
1512 } else {
1513 errmsg = "No such mempool or blockchain transaction.";
1514 }
1516 }
1517
1518 if (!pindex) {
1519 LOCK(cs_main);
1520 pindex = chainman.m_blockman.LookupBlockIndex(hash_block);
1521 }
1522
1523 if (!tx) {
1524 // Tx not found, we should have raised an error at this stage
1525 return false;
1526 }
1527
1528 if (mempool.isAvalancheFinalized(txid)) {
1529 // The transaction is finalized
1530 return true;
1531 }
1532
1533 // Return true if the tx is in a finalized block
1534 return !node.mempool->exists(txid) &&
1535 chainman.ActiveChainstate().IsBlockAvalancheFinalized(
1536 pindex);
1537 },
1538 };
1539}
1540
1542 return RPCHelpMan{
1543 "reconsideravalancheproof",
1544 "Reconsider a known avalanche proof.\n",
1545 {
1547 "The hex encoded avalanche proof."},
1548 },
1549 RPCResult{
1551 "success",
1552 "Whether the proof has been successfully registered.",
1553 },
1554 RPCExamples{HelpExampleRpc("reconsideravalancheproof", "<proof hex>")},
1555 [&](const RPCHelpMan &self, const Config &config,
1556 const JSONRPCRequest &request) -> UniValue {
1557 auto proof = RCUPtr<avalanche::Proof>::make();
1558
1559 NodeContext &node = EnsureAnyNodeContext(request.context);
1561
1562 // Verify the proof. Note that this is redundant with the
1563 // verification done when adding the proof to the pool, but we get a
1564 // chance to give a better error message.
1565 verifyProofOrThrow(node, *proof, request.params[0].get_str());
1566
1567 // There is no way to selectively clear the invalidation status of
1568 // a single proof, so we clear the whole Bloom filter. This could
1569 // cause extra voting rounds.
1570 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
1571 if (pm.isInvalid(proof->getId())) {
1572 pm.clearAllInvalid();
1573 }
1574 });
1575
1576 // Add the proof to the pool if we don't have it already. Since the
1577 // proof verification has already been done, a failure likely
1578 // indicates that there already is a proof with conflicting utxos.
1580 if (!registerProofIfNeeded(avalanche, proof, state)) {
1582 strprintf("%s (%s)\n",
1583 state.GetRejectReason(),
1584 state.GetDebugMessage()));
1585 }
1586
1587 return avalanche.withPeerManager(
1588 [&](const avalanche::PeerManager &pm) {
1589 return pm.isBoundToPeer(proof->getId());
1590 });
1591 },
1592 };
1593}
1594
1596 return RPCHelpMan{
1597 "sendavalancheproof",
1598 "Broadcast an avalanche proof.\n",
1599 {
1601 "The avalanche proof to broadcast."},
1602 },
1604 "Whether the proof was sent successfully or not."},
1605 RPCExamples{HelpExampleRpc("sendavalancheproof", "<proof>")},
1606 [&](const RPCHelpMan &self, const Config &config,
1607 const JSONRPCRequest &request) -> UniValue {
1608 auto proof = RCUPtr<avalanche::Proof>::make();
1609
1610 NodeContext &node = EnsureAnyNodeContext(request.context);
1612
1613 // Verify the proof. Note that this is redundant with the
1614 // verification done when adding the proof to the pool, but we get a
1615 // chance to give a better error message.
1616 verifyProofOrThrow(node, *proof, request.params[0].get_str());
1617
1618 // Add the proof to the pool if we don't have it already. Since the
1619 // proof verification has already been done, a failure likely
1620 // indicates that there already is a proof with conflicting utxos.
1621 const avalanche::ProofId &proofid = proof->getId();
1623 if (!registerProofIfNeeded(avalanche, proof, state)) {
1625 strprintf("%s (%s)\n",
1626 state.GetRejectReason(),
1627 state.GetDebugMessage()));
1628 }
1629
1630 avalanche.withPeerManager([&](avalanche::PeerManager &pm) {
1631 pm.addUnbroadcastProof(proofid);
1632 });
1633
1634 if (node.peerman) {
1635 node.peerman->RelayProof(proofid);
1636 }
1637
1638 return true;
1639 },
1640 };
1641}
1642
1644 return RPCHelpMan{
1645 "verifyavalancheproof",
1646 "Verify an avalanche proof is valid and return the error otherwise.\n",
1647 {
1649 "Proof to verify."},
1650 },
1652 "Whether the proof is valid or not."},
1653 RPCExamples{HelpExampleRpc("verifyavalancheproof", "\"<proof>\"")},
1654 [&](const RPCHelpMan &self, const Config &config,
1655 const JSONRPCRequest &request) -> UniValue {
1656 avalanche::Proof proof;
1657 verifyProofOrThrow(EnsureAnyNodeContext(request.context), proof,
1658 request.params[0].get_str());
1659
1660 return true;
1661 },
1662 };
1663}
1664
1666 return RPCHelpMan{
1667 "verifyavalanchedelegation",
1668 "Verify an avalanche delegation is valid and return the error "
1669 "otherwise.\n",
1670 {
1672 "The avalanche proof delegation to verify."},
1673 },
1675 "Whether the delegation is valid or not."},
1676 RPCExamples{HelpExampleRpc("verifyavalanchedelegation", "\"<proof>\"")},
1677 [&](const RPCHelpMan &self, const Config &config,
1678 const JSONRPCRequest &request) -> UniValue {
1679 avalanche::Delegation delegation;
1680 CPubKey dummy;
1681 verifyDelegationOrThrow(delegation, request.params[0].get_str(),
1682 dummy);
1683
1684 return true;
1685 },
1686 };
1687}
1688
1690 return RPCHelpMan{
1691 "setflakyproof",
1692 "Add or remove a proofid from the flaky list. This means that an "
1693 "additional staking reward winner will be accepted if this proof is "
1694 "the selected one.\n",
1695 {
1697 "The avalanche proof id."},
1699 "Whether to add (true) or remove (false) the proof from the flaky "
1700 "list"},
1701 },
1703 "Whether the addition/removal is successful."},
1704 RPCExamples{HelpExampleRpc("setflakyproof", "\"<proofid>\" true")},
1705 [&](const RPCHelpMan &self, const Config &config,
1706 const JSONRPCRequest &request) -> UniValue {
1707 NodeContext &node = EnsureAnyNodeContext(request.context);
1710
1711 const auto proofid =
1712 avalanche::ProofId::fromHex(request.params[0].get_str());
1713 const bool addNotRemove = request.params[1].get_bool();
1714
1715 if (avalanche.withPeerManager(
1716 [&proofid, addNotRemove](avalanche::PeerManager &pm) {
1717 if (addNotRemove) {
1718 return pm.setFlaky(proofid);
1719 }
1720 return pm.unsetFlaky(proofid);
1721 })) {
1722 const CBlockIndex *pprev =
1723 WITH_LOCK(cs_main, return chainman.ActiveTip());
1724 // Force recompute the staking reward winner by first erasing
1725 // the cached entry if any
1726 avalanche.eraseStakingRewardWinner(pprev->GetBlockHash());
1727 return avalanche.computeStakingReward(pprev);
1728 }
1729
1730 return false;
1731 }};
1732}
1733
1735 return RPCHelpMan{
1736 "getflakyproofs",
1737 "List the flaky proofs (set via setflakyproof).\n",
1738 {},
1739 RPCResult{
1741 "flaky_proofs",
1742 "",
1743 {{
1745 "proof",
1746 "",
1747 {{
1748 {RPCResult::Type::STR_HEX, "proofid",
1749 "The hex encoded proof identifier."},
1750 {RPCResult::Type::STR_AMOUNT, "staked_amount",
1751 "The proof stake amount, only present if the proof is "
1752 "known."},
1754 "payout",
1755 "The proof payout script, only present if the proof is "
1756 "known.",
1757 {
1758 {RPCResult::Type::STR, "asm", "Decoded payout script"},
1760 "Raw payout script in hex format"},
1761 {RPCResult::Type::STR, "type",
1762 "The output type (e.g. " + GetAllOutputTypes() + ")"},
1763 {RPCResult::Type::NUM, "reqSigs",
1764 "The required signatures"},
1766 "addresses",
1767 "",
1768 {
1769 {RPCResult::Type::STR, "address",
1770 "eCash address"},
1771 }},
1772 }},
1773 }},
1774 }},
1775 },
1776 RPCExamples{HelpExampleRpc("getflakyproofs", "")},
1777 [&](const RPCHelpMan &self, const Config &config,
1778 const JSONRPCRequest &request) -> UniValue {
1779 NodeContext &node = EnsureAnyNodeContext(request.context);
1781
1783 avalanche.withPeerManager([&flakyProofs](
1785 pm.forEachFlakyProof([&](const avalanche::ProofId &proofid) {
1787 flakyProof.pushKV("proofid", proofid.GetHex());
1788
1789 const auto proof = pm.getProof(proofid);
1790 if (proof) {
1791 flakyProof.pushKV("staked_amount",
1792 proof->getStakedAmount());
1794 ScriptPubKeyToUniv(proof->getPayoutScript(), payout,
1795 /*fIncludeHex=*/true);
1796 flakyProof.pushKV("payout", payout);
1797 }
1798
1799 flakyProofs.push_back(flakyProof);
1800 });
1801 });
1802
1803 return flakyProofs;
1804 }};
1805}
1806
1808 return RPCHelpMan{
1809 "getstakecontendervote",
1810 "Return the stake contender avalanche vote.\n",
1811 {
1813 "The prevblockhash used to compute the stake contender ID, hex "
1814 "encoded."},
1816 "The proofid used to compute the stake contender ID, hex "
1817 "encoded."},
1818 },
1820 "The vote that would be returned if polled."},
1821 RPCExamples{HelpExampleRpc("getstakecontendervote",
1822 "<prevblockhash> <proofid>")},
1823 [&](const RPCHelpMan &self, const Config &config,
1824 const JSONRPCRequest &request) -> UniValue {
1825 const NodeContext &node = EnsureAnyNodeContext(request.context);
1827
1828 const BlockHash prevblockhash(
1829 ParseHashV(request.params[0], "prevblockhash"));
1830 const avalanche::ProofId proofid(
1831 ParseHashV(request.params[1], "proofid"));
1832 const avalanche::StakeContenderId contenderId(prevblockhash,
1833 proofid);
1834 return avalanche.getStakeContenderStatus(contenderId);
1835 },
1836 };
1837}
1838
1840 // clang-format off
1841 static const CRPCCommand commands[] = {
1842 // category actor (function)
1843 // ----------------- --------------------
1844 { "avalanche", getavalanchekey, },
1845 { "avalanche", addavalanchenode, },
1846 { "avalanche", buildavalancheproof, },
1847 { "avalanche", decodeavalancheproof, },
1848 { "avalanche", delegateavalancheproof, },
1849 { "avalanche", decodeavalanchedelegation, },
1850 { "avalanche", getavalancheinfo, },
1851 { "avalanche", getavalanchepeerinfo, },
1852 { "avalanche", getavalancheproofs, },
1853 { "avalanche", getstakingreward, },
1854 { "hidden", hasstakingreward, },
1855 { "avalanche", setstakingreward, },
1856 { "avalanche", getremoteproofs, },
1857 { "avalanche", getrawavalancheproof, },
1858 { "avalanche", invalidateavalancheproof, },
1859 { "avalanche", isfinalblock, },
1860 { "avalanche", isfinaltransaction, },
1861 { "avalanche", reconsideravalancheproof, },
1862 { "avalanche", sendavalancheproof, },
1863 { "avalanche", verifyavalancheproof, },
1864 { "avalanche", verifyavalanchedelegation, },
1865 { "avalanche", setflakyproof, },
1866 { "avalanche", getflakyproofs, },
1867 { "hidden", getstakecontendervote, },
1868 };
1869 // clang-format on
1870
1871 for (const auto &c : commands) {
1872 t.appendCommand(c.name, &c);
1873 }
1874}
static RPCHelpMan buildavalancheproof()
static RPCHelpMan invalidateavalancheproof()
static RPCHelpMan delegateavalancheproof()
static RPCHelpMan getremoteproofs()
static RPCHelpMan decodeavalanchedelegation()
static RPCHelpMan sendavalancheproof()
static RPCHelpMan getavalancheproofs()
static void verifyDelegationOrThrow(avalanche::Delegation &dg, const std::string &dgHex, CPubKey &auth)
Definition avalanche.cpp:81
static RPCHelpMan getrawavalancheproof()
static void verifyProofOrThrow(const NodeContext &node, avalanche::Proof &proof, const std::string &proofHex)
Definition avalanche.cpp:95
void RegisterAvalancheRPCCommands(CRPCTable &t)
static RPCHelpMan getavalanchekey()
Definition avalanche.cpp:34
static RPCHelpMan hasstakingreward()
static RPCHelpMan addavalanchenode()
static CPubKey ParsePubKey(const UniValue &param)
Definition avalanche.cpp:50
static RPCHelpMan getstakecontendervote()
static RPCHelpMan verifyavalanchedelegation()
static RPCHelpMan setflakyproof()
static RPCHelpMan setstakingreward()
static RPCHelpMan getflakyproofs()
static RPCHelpMan isfinalblock()
static RPCHelpMan reconsideravalancheproof()
static RPCHelpMan isfinaltransaction()
static RPCHelpMan getstakingreward()
static bool registerProofIfNeeded(const avalanche::Processor &avalanche, avalanche::ProofRef proof, avalanche::ProofRegistrationState &state)
Definition avalanche.cpp:62
static RPCHelpMan getavalanchepeerinfo()
static RPCHelpMan verifyavalancheproof()
static RPCHelpMan getavalancheinfo()
static RPCHelpMan decodeavalancheproof()
#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
Information about a peer.
Definition net.h:460
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition transaction.h:20
const TxId & GetTxId() const
Definition transaction.h:35
uint32_t GetN() const
Definition transaction.h:36
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
Serialized script, used inside transaction inputs and outputs.
Definition script.h:424
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...
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...
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
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 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
const std::vector< Level > & getLevels() const
Definition delegation.h:64
static bool FromHex(Delegation &dg, const std::string &dgHex, bilingual_str &errorOut)
const CPubKey & getProofMaster() const
Definition delegation.h:62
const DelegationId & getId() const
Definition delegation.h:60
const CPubKey & getDelegatedPubkey() const
const LimitedProofId & getLimitedProofId() const
Definition delegation.h:61
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
ProofIdSet getProofIds() const
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)
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:263
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
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
Definition random.h:85
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)
NodeContext & EnsureAnyNodeContext(const std::any &context)
CTxMemPool & EnsureMemPool(const NodeContext &node)
ChainstateManager & EnsureChainman(const NodeContext &node)
avalanche::Processor & EnsureAvalanche(const NodeContext &node)
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
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
static ProofId fromHex(const std::string &str)
Definition proofid.h:21
StakeContenderIds are unique for each block to ensure that the peer polling for their acceptance has ...
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...
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