Bitcoin ABC  0.26.3
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 
5 #include <avalanche/avalanche.h>
6 #include <avalanche/delegation.h>
9 #include <avalanche/processor.h>
10 #include <avalanche/proof.h>
11 #include <avalanche/proofbuilder.h>
12 #include <avalanche/validation.h>
13 #include <config.h>
14 #include <core_io.h>
15 #include <index/txindex.h>
16 #include <key_io.h>
17 #include <net_processing.h>
18 #include <node/context.h>
20 #include <rpc/blockchain.h>
21 #include <rpc/server.h>
22 #include <rpc/server_util.h>
23 #include <rpc/util.h>
24 #include <util/strencodings.h>
25 #include <util/translation.h>
26 
27 #include <univalue.h>
28 
30 using node::NodeContext;
31 
33  return RPCHelpMan{
34  "getavalanchekey",
35  "Returns the key used to sign avalanche messages.\n",
36  {},
38  RPCExamples{HelpExampleRpc("getavalanchekey", "")},
39  [&](const RPCHelpMan &self, const Config &config,
40  const JSONRPCRequest &request) -> UniValue {
41  if (!g_avalanche) {
43  "Avalanche is not initialized");
44  }
45 
46  return HexStr(g_avalanche->getSessionPubKey());
47  },
48  };
49 }
50 
51 static CPubKey ParsePubKey(const UniValue &param) {
52  const std::string keyHex = param.get_str();
53  if ((keyHex.length() != 2 * CPubKey::COMPRESSED_SIZE &&
54  keyHex.length() != 2 * CPubKey::SIZE) ||
55  !IsHex(keyHex)) {
57  strprintf("Invalid public key: %s\n", keyHex));
58  }
59 
60  return HexToPubKey(keyHex);
61 }
62 
65  auto localProof = g_avalanche->getLocalProof();
66  if (localProof && localProof->getId() == proof->getId()) {
67  return true;
68  }
69 
70  return g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
71  return pm.getProof(proof->getId()) ||
72  pm.registerProof(std::move(proof), state);
73  });
74 }
75 
78  return registerProofIfNeeded(std::move(proof), state);
79 }
80 
82  const std::string &dgHex, CPubKey &auth) {
84  if (!avalanche::Delegation::FromHex(dg, dgHex, error)) {
86  }
87 
89  if (!dg.verify(state, auth)) {
91  "The delegation is invalid: " + state.ToString());
92  }
93 }
94 
96  const std::string &proofHex) {
98  if (!avalanche::Proof::FromHex(proof, proofHex, error)) {
100  }
101 
102  Amount stakeUtxoDustThreshold = avalanche::PROOF_DUST_THRESHOLD;
103  if (g_avalanche) {
104  // If Avalanche is enabled, use the configured dust threshold
105  g_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  },
135  RPCResult{RPCResult::Type::BOOL, "success",
136  "Whether the addition succeeded or not."},
137  RPCExamples{
138  HelpExampleRpc("addavalanchenode", "5, \"<pubkey>\", \"<proof>\"")},
139  [&](const RPCHelpMan &self, const Config &config,
140  const JSONRPCRequest &request) -> UniValue {
141  RPCTypeCheck(request.params,
142  {UniValue::VNUM, UniValue::VSTR, UniValue::VSTR});
143 
144  if (!g_avalanche) {
146  "Avalanche is not initialized");
147  }
148 
149  const NodeId nodeid = request.params[0].get_int64();
150  CPubKey key = ParsePubKey(request.params[1]);
151 
152  auto proof = RCUPtr<avalanche::Proof>::make();
153  NodeContext &node = EnsureAnyNodeContext(request.context);
154  verifyProofOrThrow(node, *proof, request.params[2].get_str());
155 
156  const avalanche::ProofId &proofid = proof->getId();
157  if (key != proof->getMaster()) {
158  if (request.params.size() < 4 || request.params[3].isNull()) {
159  throw JSONRPCError(
161  "The public key does not match the proof");
162  }
163 
165  CPubKey auth;
166  verifyDelegationOrThrow(dg, request.params[3].get_str(), auth);
167 
168  if (dg.getProofId() != proofid) {
169  throw JSONRPCError(
171  "The delegation does not match the proof");
172  }
173 
174  if (key != auth) {
175  throw JSONRPCError(
177  "The public key does not match the delegation");
178  }
179  }
180 
181  if (!registerProofIfNeeded(proof)) {
183  "The proof has conflicting utxos");
184  }
185 
186  if (!node.connman->ForNode(nodeid, [&](CNode *pnode) {
187  LOCK(pnode->cs_avalanche_pubkey);
188  bool expected = false;
189  if (pnode->m_avalanche_enabled.compare_exchange_strong(
190  expected, true)) {
191  pnode->m_avalanche_pubkey = std::move(key);
192  }
193  return true;
194  })) {
195  throw JSONRPCError(
197  strprintf("The node does not exist: %d", nodeid));
198  }
199 
200  return g_avalanche->withPeerManager(
201  [&](avalanche::PeerManager &pm) {
202  if (!pm.addNode(nodeid, proofid)) {
203  return false;
204  }
205 
206  pm.addUnbroadcastProof(proofid);
207  return true;
208  });
209  },
210  };
211 }
212 
214  return RPCHelpMan{
215  "buildavalancheproof",
216  "Build a proof for avalanche's sybil resistance.\n",
217  {
219  "The proof's sequence"},
220  {"expiration", RPCArg::Type::NUM, RPCArg::Optional::NO,
221  "A timestamp indicating when the proof expire"},
223  "The master private key in base58-encoding"},
224  {
225  "stakes",
228  "The stakes to be signed and associated private keys",
229  {
230  {
231  "stake",
234  "A stake to be attached to this proof",
235  {
236  {"txid", RPCArg::Type::STR_HEX,
237  RPCArg::Optional::NO, "The transaction id"},
239  "The output number"},
240  {"amount", RPCArg::Type::AMOUNT,
241  RPCArg::Optional::NO, "The amount in this UTXO"},
243  "The height at which this UTXO was mined"},
244  {"iscoinbase", RPCArg::Type::BOOL,
245  RPCArg::Default{false},
246  "Indicate wether the UTXO is a coinbase"},
247  {"privatekey", RPCArg::Type::STR,
249  "private key in base58-encoding"},
250  },
251  },
252  },
253  },
254  {"payoutAddress", RPCArg::Type::STR, RPCArg::Optional::NO,
255  "A payout address"},
256  },
258  "A string that is a serialized, hex-encoded proof data."},
259  RPCExamples{HelpExampleRpc("buildavalancheproof",
260  "0 1234567800 \"<master>\" []")},
261  [&](const RPCHelpMan &self, const Config &config,
262  const JSONRPCRequest &request) -> UniValue {
263  RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM,
264  UniValue::VSTR, UniValue::VARR});
265 
266  const uint64_t sequence = request.params[0].get_int64();
267  const int64_t expiration = request.params[1].get_int64();
268 
269  CKey masterKey = DecodeSecret(request.params[2].get_str());
270  if (!masterKey.IsValid()) {
271  throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid master key");
272  }
273 
274  CTxDestination payoutAddress = DecodeDestination(
275  request.params[4].get_str(), config.GetChainParams());
276 
277  if (!IsValidDestination(payoutAddress)) {
279  "Invalid payout address");
280  }
281 
282  avalanche::ProofBuilder pb(sequence, expiration, masterKey,
283  GetScriptForDestination(payoutAddress));
284 
285  const UniValue &stakes = request.params[3].get_array();
286  for (size_t i = 0; i < stakes.size(); i++) {
287  const UniValue &stake = stakes[i];
289  stake,
290  {
291  {"txid", UniValue::VSTR},
292  {"vout", UniValue::VNUM},
293  // "amount" is also required but check is done below
294  // due to UniValue::VNUM erroneously not accepting
295  // quoted numerics (which are valid JSON)
296  {"height", UniValue::VNUM},
297  {"privatekey", UniValue::VSTR},
298  });
299 
300  int nOut = stake.find_value("vout").get_int();
301  if (nOut < 0) {
303  "vout cannot be negative");
304  }
305 
306  const int height = stake.find_value("height").get_int();
307  if (height < 1) {
309  "height must be positive");
310  }
311 
312  const TxId txid(ParseHashO(stake, "txid"));
313  const COutPoint utxo(txid, nOut);
314 
315  if (!stake.exists("amount")) {
316  throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount");
317  }
318 
319  const Amount amount =
320  AmountFromValue(stake.find_value("amount"));
321 
322  const UniValue &iscbparam = stake.find_value("iscoinbase");
323  const bool iscoinbase =
324  iscbparam.isNull() ? false : iscbparam.get_bool();
325  CKey key =
326  DecodeSecret(stake.find_value("privatekey").get_str());
327 
328  if (!key.IsValid()) {
330  "Invalid private key");
331  }
332 
333  if (!pb.addUTXO(utxo, amount, uint32_t(height), iscoinbase,
334  std::move(key))) {
336  "Duplicated stake");
337  }
338  }
339 
340  const avalanche::ProofRef proof = pb.build();
341 
342  return proof->ToHex();
343  },
344  };
345 }
346 
348  return RPCHelpMan{
349  "decodeavalancheproof",
350  "Convert a serialized, hex-encoded proof, into JSON object. "
351  "The validity of the proof is not verified.\n",
352  {
354  "The proof hex string"},
355  },
356  RPCResult{
358  "",
359  "",
360  {
361  {RPCResult::Type::NUM, "sequence",
362  "The proof's sequential number"},
363  {RPCResult::Type::NUM, "expiration",
364  "A timestamp indicating when the proof expires"},
365  {RPCResult::Type::STR_HEX, "master", "The master public key"},
366  {RPCResult::Type::STR, "signature",
367  "The proof signature (base64 encoded)"},
369  "payoutscript",
370  "The proof payout script",
371  {
372  {RPCResult::Type::STR, "asm", "Decoded payout script"},
373  {RPCResult::Type::STR_HEX, "hex",
374  "Raw payout script in hex format"},
375  {RPCResult::Type::STR, "type",
376  "The output type (e.g. " + GetAllOutputTypes() + ")"},
377  {RPCResult::Type::NUM, "reqSigs",
378  "The required signatures"},
380  "addresses",
381  "",
382  {
383  {RPCResult::Type::STR, "address", "eCash address"},
384  }},
385  }},
386  {RPCResult::Type::STR_HEX, "limitedid",
387  "A hash of the proof data excluding the master key."},
388  {RPCResult::Type::STR_HEX, "proofid",
389  "A hash of the limitedid and master key."},
390  {RPCResult::Type::STR_AMOUNT, "staked_amount",
391  "The total staked amount of this proof in " +
392  Currency::get().ticker + "."},
393  {RPCResult::Type::NUM, "score", "The score of this proof."},
395  "stakes",
396  "",
397  {
399  "",
400  "",
401  {
402  {RPCResult::Type::STR_HEX, "txid",
403  "The transaction id"},
404  {RPCResult::Type::NUM, "vout", "The output number"},
405  {RPCResult::Type::STR_AMOUNT, "amount",
406  "The amount in this UTXO"},
407  {RPCResult::Type::NUM, "height",
408  "The height at which this UTXO was mined"},
409  {RPCResult::Type::BOOL, "iscoinbase",
410  "Indicate whether the UTXO is a coinbase"},
411  {RPCResult::Type::STR_HEX, "pubkey",
412  "This UTXO's public key"},
413  {RPCResult::Type::STR, "signature",
414  "Signature of the proofid with this UTXO's private "
415  "key (base64 encoded)"},
416  }},
417  }},
418  }},
419  RPCExamples{HelpExampleCli("decodeavalancheproof", "\"<hex proof>\"") +
420  HelpExampleRpc("decodeavalancheproof", "\"<hex proof>\"")},
421  [&](const RPCHelpMan &self, const Config &config,
422  const JSONRPCRequest &request) -> UniValue {
423  RPCTypeCheck(request.params, {UniValue::VSTR});
424 
425  avalanche::Proof proof;
427  if (!avalanche::Proof::FromHex(proof, request.params[0].get_str(),
428  error)) {
430  }
431 
432  UniValue result(UniValue::VOBJ);
433  result.pushKV("sequence", proof.getSequence());
434  result.pushKV("expiration", proof.getExpirationTime());
435  result.pushKV("master", HexStr(proof.getMaster()));
436  result.pushKV("signature", EncodeBase64(proof.getSignature()));
437 
438  const auto payoutScript = proof.getPayoutScript();
439  UniValue payoutScriptObj(UniValue::VOBJ);
440  ScriptPubKeyToUniv(payoutScript, payoutScriptObj,
441  /* fIncludeHex */ true);
442  result.pushKV("payoutscript", payoutScriptObj);
443 
444  result.pushKV("limitedid", proof.getLimitedId().ToString());
445  result.pushKV("proofid", proof.getId().ToString());
446 
447  result.pushKV("staked_amount", proof.getStakedAmount());
448  result.pushKV("score", uint64_t(proof.getScore()));
449 
450  UniValue stakes(UniValue::VARR);
451  for (const avalanche::SignedStake &s : proof.getStakes()) {
452  const COutPoint &utxo = s.getStake().getUTXO();
453  UniValue stake(UniValue::VOBJ);
454  stake.pushKV("txid", utxo.GetTxId().ToString());
455  stake.pushKV("vout", uint64_t(utxo.GetN()));
456  stake.pushKV("amount", s.getStake().getAmount());
457  stake.pushKV("height", uint64_t(s.getStake().getHeight()));
458  stake.pushKV("iscoinbase", s.getStake().isCoinbase());
459  stake.pushKV("pubkey", HexStr(s.getStake().getPubkey()));
460  // Only PKHash destination is supported, so this is safe
461  stake.pushKV("address",
462  EncodeDestination(PKHash(s.getStake().getPubkey()),
463  config));
464  stake.pushKV("signature", EncodeBase64(s.getSignature()));
465  stakes.push_back(stake);
466  }
467  result.pushKV("stakes", stakes);
468 
469  return result;
470  },
471  };
472 }
473 
475  return RPCHelpMan{
476  "delegateavalancheproof",
477  "Delegate the avalanche proof to another public key.\n",
478  {
479  {"limitedproofid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO,
480  "The limited id of the proof to be delegated."},
481  {"privatekey", RPCArg::Type::STR, RPCArg::Optional::NO,
482  "The private key in base58-encoding. Must match the proof master "
483  "public key or the upper level parent delegation public key if "
484  " supplied."},
486  "The public key to delegate the proof to."},
488  "A string that is the serialized, hex-encoded delegation for the "
489  "proof and which is a parent for the delegation to build."},
490  },
491  RPCResult{RPCResult::Type::STR_HEX, "delegation",
492  "A string that is a serialized, hex-encoded delegation."},
493  RPCExamples{
494  HelpExampleRpc("delegateavalancheproof",
495  "\"<limitedproofid>\" \"<privkey>\" \"<pubkey>\"")},
496  [&](const RPCHelpMan &self, const Config &config,
497  const JSONRPCRequest &request) -> UniValue {
498  RPCTypeCheck(request.params,
499  {UniValue::VSTR, UniValue::VSTR, UniValue::VSTR});
500 
501  if (!g_avalanche) {
503  "Avalanche is not initialized");
504  }
505 
506  avalanche::LimitedProofId limitedProofId{
507  ParseHashV(request.params[0], "limitedproofid")};
508 
509  const CKey privkey = DecodeSecret(request.params[1].get_str());
510  if (!privkey.IsValid()) {
512  "The private key is invalid");
513  }
514 
515  const CPubKey pubkey = ParsePubKey(request.params[2]);
516 
517  std::unique_ptr<avalanche::DelegationBuilder> dgb;
518  if (request.params.size() >= 4 && !request.params[3].isNull()) {
520  CPubKey auth;
521  verifyDelegationOrThrow(dg, request.params[3].get_str(), auth);
522 
523  if (dg.getProofId() !=
524  limitedProofId.computeProofId(dg.getProofMaster())) {
525  throw JSONRPCError(
527  "The delegation does not match the proof");
528  }
529 
530  if (privkey.GetPubKey() != auth) {
531  throw JSONRPCError(
533  "The private key does not match the delegation");
534  }
535 
536  dgb = std::make_unique<avalanche::DelegationBuilder>(dg);
537  } else {
538  dgb = std::make_unique<avalanche::DelegationBuilder>(
539  limitedProofId, privkey.GetPubKey());
540  }
541 
542  if (!dgb->addLevel(privkey, pubkey)) {
544  "Unable to build the delegation");
545  }
546 
548  ss << dgb->build();
549  return HexStr(ss);
550  },
551  };
552 }
553 
555  return RPCHelpMan{
556  "decodeavalanchedelegation",
557  "Convert a serialized, hex-encoded avalanche proof delegation, into "
558  "JSON object. \n"
559  "The validity of the delegation is not verified.\n",
560  {
562  "The delegation hex string"},
563  },
564  RPCResult{
566  "",
567  "",
568  {
569  {RPCResult::Type::STR_HEX, "pubkey",
570  "The public key the proof is delegated to."},
571  {RPCResult::Type::STR_HEX, "proofmaster",
572  "The delegated proof master public key."},
573  {RPCResult::Type::STR_HEX, "delegationid",
574  "The identifier of this delegation."},
575  {RPCResult::Type::STR_HEX, "limitedid",
576  "A delegated proof data hash excluding the master key."},
577  {RPCResult::Type::STR_HEX, "proofid",
578  "A hash of the delegated proof limitedid and master key."},
579  {RPCResult::Type::NUM, "depth",
580  "The number of delegation levels."},
582  "levels",
583  "",
584  {
586  "",
587  "",
588  {
589  {RPCResult::Type::NUM, "index",
590  "The index of this delegation level."},
591  {RPCResult::Type::STR_HEX, "pubkey",
592  "This delegated public key for this level"},
593  {RPCResult::Type::STR, "signature",
594  "Signature of this delegation level (base64 "
595  "encoded)"},
596  }},
597  }},
598  }},
599  RPCExamples{HelpExampleCli("decodeavalanchedelegation",
600  "\"<hex delegation>\"") +
601  HelpExampleRpc("decodeavalanchedelegation",
602  "\"<hex delegation>\"")},
603  [&](const RPCHelpMan &self, const Config &config,
604  const JSONRPCRequest &request) -> UniValue {
605  RPCTypeCheck(request.params, {UniValue::VSTR});
606 
607  avalanche::Delegation delegation;
610  delegation, request.params[0].get_str(), error)) {
612  }
613 
614  UniValue result(UniValue::VOBJ);
615  result.pushKV("pubkey", HexStr(delegation.getDelegatedPubkey()));
616  result.pushKV("proofmaster", HexStr(delegation.getProofMaster()));
617  result.pushKV("delegationid", delegation.getId().ToString());
618  result.pushKV("limitedid",
619  delegation.getLimitedProofId().ToString());
620  result.pushKV("proofid", delegation.getProofId().ToString());
621 
622  auto levels = delegation.getLevels();
623  result.pushKV("depth", uint64_t(levels.size()));
624 
625  UniValue levelsArray(UniValue::VARR);
626  for (auto &level : levels) {
628  obj.pushKV("pubkey", HexStr(level.pubkey));
629  obj.pushKV("signature", EncodeBase64(level.sig));
630  levelsArray.push_back(std::move(obj));
631  }
632  result.pushKV("levels", levelsArray);
633 
634  return result;
635  },
636  };
637 }
638 
640  return RPCHelpMan{
641  "getavalancheinfo",
642  "Returns an object containing various state info regarding avalanche "
643  "networking.\n",
644  {},
645  RPCResult{
647  "",
648  "",
649  {
650  {RPCResult::Type::BOOL, "ready_to_poll",
651  "Whether the node is ready to start polling and voting."},
653  "local",
654  "Only available if -avaproof has been supplied to the node",
655  {
656  {RPCResult::Type::BOOL, "verified",
657  "Whether the node local proof has been locally verified "
658  "or not."},
659  {RPCResult::Type::STR, "verification_status",
660  "The proof verification status. Only available if the "
661  "\"verified\" flag is false."},
662  {RPCResult::Type::BOOL, "sharing",
663  "DEPRECATED: Whether the node local proof is being "
664  "advertised on the network or not. Only displayed if the "
665  "-deprecatedrpc=getavalancheinfo_sharing option is set."},
666  {RPCResult::Type::STR_HEX, "proofid",
667  "The node local proof id."},
668  {RPCResult::Type::STR_HEX, "limited_proofid",
669  "The node local limited proof id."},
670  {RPCResult::Type::STR_HEX, "master",
671  "The node local proof master public key."},
672  {RPCResult::Type::STR, "payout_address",
673  "The node local proof payout address. This might be "
674  "omitted if the payout script is not one of P2PK, P2PKH "
675  "or P2SH, in which case decodeavalancheproof can be used "
676  "to get more details."},
677  {RPCResult::Type::STR_AMOUNT, "stake_amount",
678  "The node local proof staked amount."},
679  }},
681  "network",
682  "",
683  {
684  {RPCResult::Type::NUM, "proof_count",
685  "The number of valid avalanche proofs we know exist "
686  "(including this node's local proof if applicable)."},
687  {RPCResult::Type::NUM, "connected_proof_count",
688  "The number of avalanche proofs with at least one node "
689  "we are connected to (including this node's local proof "
690  "if applicable)."},
691  {RPCResult::Type::NUM, "dangling_proof_count",
692  "The number of avalanche proofs with no node attached."},
693  {RPCResult::Type::NUM, "finalized_proof_count",
694  "The number of known avalanche proofs that have been "
695  "finalized by avalanche."},
696  {RPCResult::Type::NUM, "conflicting_proof_count",
697  "The number of known avalanche proofs that conflict with "
698  "valid proofs."},
699  {RPCResult::Type::NUM, "immature_proof_count",
700  "The number of known avalanche proofs that have immature "
701  "utxos."},
702  {RPCResult::Type::STR_AMOUNT, "total_stake_amount",
703  "The total staked amount over all the valid proofs in " +
705  " (including this node's local proof if "
706  "applicable)."},
707  {RPCResult::Type::STR_AMOUNT, "connected_stake_amount",
708  "The total staked amount over all the connected proofs "
709  "in " +
711  " (including this node's local proof if "
712  "applicable)."},
713  {RPCResult::Type::STR_AMOUNT, "dangling_stake_amount",
714  "The total staked amount over all the dangling proofs "
715  "in " +
717  " (including this node's local proof if "
718  "applicable)."},
719  {RPCResult::Type::STR_AMOUNT, "immature_stake_amount",
720  "The total staked amount over all the immature proofs "
721  "in " +
723  " (including this node's local proof if "
724  "applicable)."},
725  {RPCResult::Type::NUM, "node_count",
726  "The number of avalanche nodes we are connected to "
727  "(including this node if a local proof is set)."},
728  {RPCResult::Type::NUM, "connected_node_count",
729  "The number of avalanche nodes associated with an "
730  "avalanche proof (including this node if a local proof "
731  "is set)."},
732  {RPCResult::Type::NUM, "pending_node_count",
733  "The number of avalanche nodes pending for a proof."},
734  }},
735  },
736  },
737  RPCExamples{HelpExampleCli("getavalancheinfo", "") +
738  HelpExampleRpc("getavalancheinfo", "")},
739  [&](const RPCHelpMan &self, const Config &config,
740  const JSONRPCRequest &request) -> UniValue {
741  if (!g_avalanche) {
743  "Avalanche is not initialized");
744  }
745 
747  ret.pushKV("ready_to_poll", g_avalanche->isQuorumEstablished());
748 
749  auto localProof = g_avalanche->getLocalProof();
750  if (localProof != nullptr) {
751  UniValue local(UniValue::VOBJ);
752  const bool verified = g_avalanche->withPeerManager(
753  [&](const avalanche::PeerManager &pm) {
754  const avalanche::ProofId &proofid = localProof->getId();
755  return pm.isBoundToPeer(proofid);
756  });
757  local.pushKV("verified", verified);
758  const bool sharing = g_avalanche->canShareLocalProof();
759  if (!verified) {
761  g_avalanche->getLocalProofRegistrationState();
762  // If the local proof is not registered but the state is
763  // valid, no registration attempt occurred yet.
764  local.pushKV("verification_status",
765  state.IsValid()
766  ? (sharing ? "pending verification"
767  : "pending inbound connections")
768  : state.GetRejectReason());
769  }
770  if (IsDeprecatedRPCEnabled(gArgs, "getavalancheinfo_sharing")) {
771  local.pushKV("sharing", sharing);
772  }
773  local.pushKV("proofid", localProof->getId().ToString());
774  local.pushKV("limited_proofid",
775  localProof->getLimitedId().ToString());
776  local.pushKV("master", HexStr(localProof->getMaster()));
777  CTxDestination destination;
778  if (ExtractDestination(localProof->getPayoutScript(),
779  destination)) {
780  local.pushKV("payout_address",
781  EncodeDestination(destination, config));
782  }
783  local.pushKV("stake_amount", localProof->getStakedAmount());
784  ret.pushKV("local", local);
785  }
786 
787  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
788  UniValue network(UniValue::VOBJ);
789 
790  uint64_t proofCount{0};
791  uint64_t connectedProofCount{0};
792  uint64_t finalizedProofCount{0};
793  uint64_t connectedNodeCount{0};
794  Amount totalStakes = Amount::zero();
795  Amount connectedStakes = Amount::zero();
796 
797  pm.forEachPeer([&](const avalanche::Peer &peer) {
798  CHECK_NONFATAL(peer.proof != nullptr);
799 
800  const bool isLocalProof =
801  localProof &&
802  peer.proof->getId() == localProof->getId();
803 
804  ++proofCount;
805  const Amount proofStake = peer.proof->getStakedAmount();
806 
807  totalStakes += proofStake;
808 
809  if (peer.hasFinalized) {
810  ++finalizedProofCount;
811  }
812 
813  if (peer.node_count > 0 || isLocalProof) {
814  ++connectedProofCount;
815  connectedStakes += proofStake;
816  }
817 
818  connectedNodeCount += peer.node_count + isLocalProof;
819  });
820 
821  Amount immatureStakes = Amount::zero();
823  [&](const avalanche::ProofRef &proof) {
824  immatureStakes += proof->getStakedAmount();
825  });
826 
827  network.pushKV("proof_count", proofCount);
828  network.pushKV("connected_proof_count", connectedProofCount);
829  network.pushKV("dangling_proof_count",
830  proofCount - connectedProofCount);
831 
832  network.pushKV("finalized_proof_count", finalizedProofCount);
833  network.pushKV(
834  "conflicting_proof_count",
835  uint64_t(pm.getConflictingProofPool().countProofs()));
836  network.pushKV(
837  "immature_proof_count",
838  uint64_t(pm.getImmatureProofPool().countProofs()));
839 
840  network.pushKV("total_stake_amount", totalStakes);
841  network.pushKV("connected_stake_amount", connectedStakes);
842  network.pushKV("dangling_stake_amount",
843  totalStakes - connectedStakes);
844  network.pushKV("immature_stake_amount", immatureStakes);
845 
846  const uint64_t pendingNodes = pm.getPendingNodeCount();
847  network.pushKV("node_count", connectedNodeCount + pendingNodes);
848  network.pushKV("connected_node_count", connectedNodeCount);
849  network.pushKV("pending_node_count", pendingNodes);
850 
851  ret.pushKV("network", network);
852  });
853 
854  return ret;
855  },
856  };
857 }
858 
860  return RPCHelpMan{
861  "getavalanchepeerinfo",
862  "Returns data about an avalanche peer as a json array of objects. If "
863  "no proofid is provided, returns data about all the peers.\n",
864  {
866  "The hex encoded avalanche proof identifier."},
867  },
868  RPCResult{
870  "",
871  "",
872  {{
874  "",
875  "",
876  {{
877  {RPCResult::Type::NUM, "avalanche_peerid",
878  "The avalanche internal peer identifier"},
879  {RPCResult::Type::NUM, "availability_score",
880  "The agreggated availability score of this peer's nodes"},
881  {RPCResult::Type::STR_HEX, "proofid",
882  "The avalanche proof id used by this peer"},
883  {RPCResult::Type::STR_HEX, "proof",
884  "The avalanche proof used by this peer"},
885  {RPCResult::Type::NUM, "nodecount",
886  "The number of nodes for this peer"},
888  "node_list",
889  "",
890  {
891  {RPCResult::Type::NUM, "nodeid",
892  "Node id, as returned by getpeerinfo"},
893  }},
894  }},
895  }},
896  },
897  RPCExamples{HelpExampleCli("getavalanchepeerinfo", "") +
898  HelpExampleCli("getavalanchepeerinfo", "\"proofid\"") +
899  HelpExampleRpc("getavalanchepeerinfo", "") +
900  HelpExampleRpc("getavalanchepeerinfo", "\"proofid\"")},
901  [&](const RPCHelpMan &self, const Config &config,
902  const JSONRPCRequest &request) -> UniValue {
903  RPCTypeCheck(request.params, {UniValue::VSTR});
904 
905  if (!g_avalanche) {
907  "Avalanche is not initialized");
908  }
909 
910  auto peerToUniv = [](const avalanche::PeerManager &pm,
911  const avalanche::Peer &peer) {
913 
914  obj.pushKV("avalanche_peerid", uint64_t(peer.peerid));
915  obj.pushKV("availability_score", peer.availabilityScore);
916  obj.pushKV("proofid", peer.getProofId().ToString());
917  obj.pushKV("proof", peer.proof->ToHex());
918 
919  UniValue nodes(UniValue::VARR);
920  pm.forEachNode(peer, [&](const avalanche::Node &n) {
921  nodes.push_back(n.nodeid);
922  });
923 
924  obj.pushKV("nodecount", uint64_t(peer.node_count));
925  obj.pushKV("node_list", nodes);
926 
927  return obj;
928  };
929 
931 
932  g_avalanche->withPeerManager([&](const avalanche::PeerManager &pm) {
933  // If a proofid is provided, only return the associated peer
934  if (!request.params[0].isNull()) {
935  const avalanche::ProofId proofid =
936  avalanche::ProofId::fromHex(
937  request.params[0].get_str());
938  if (!pm.isBoundToPeer(proofid)) {
939  throw JSONRPCError(RPC_INVALID_PARAMETER,
940  "Proofid not found");
941  }
942 
943  pm.forPeer(proofid, [&](const avalanche::Peer &peer) {
944  return ret.push_back(peerToUniv(pm, peer));
945  });
946 
947  return;
948  }
949 
950  // If no proofid is provided, return all the peers
951  pm.forEachPeer([&](const avalanche::Peer &peer) {
952  ret.push_back(peerToUniv(pm, peer));
953  });
954  });
955 
956  return ret;
957  },
958  };
959 }
960 
962  return RPCHelpMan{
963  "getavalancheproofs",
964  "Returns an object containing all tracked proofids.\n",
965  {},
966  RPCResult{
968  "",
969  "",
970  {
972  "valid",
973  "",
974  {
975  {RPCResult::Type::STR_HEX, "proofid",
976  "Avalanche proof id"},
977  }},
979  "conflicting",
980  "",
981  {
982  {RPCResult::Type::STR_HEX, "proofid",
983  "Avalanche proof id"},
984  }},
986  "immature",
987  "",
988  {
989  {RPCResult::Type::STR_HEX, "proofid",
990  "Avalanche proof id"},
991  }},
992  },
993  },
994  RPCExamples{HelpExampleCli("getavalancheproofs", "") +
995  HelpExampleRpc("getavalancheproofs", "")},
996  [&](const RPCHelpMan &self, const Config &config,
997  const JSONRPCRequest &request) -> UniValue {
998  if (!g_avalanche) {
1000  "Avalanche is not initialized");
1001  }
1002 
1003  UniValue ret(UniValue::VOBJ);
1004  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
1005  auto appendProofIds = [&ret](const avalanche::ProofPool &pool,
1006  const std::string &key) {
1007  UniValue arrOut(UniValue::VARR);
1008  for (const avalanche::ProofId &proofid :
1009  pool.getProofIds()) {
1010  arrOut.push_back(proofid.ToString());
1011  }
1012  ret.pushKV(key, arrOut);
1013  };
1014 
1015  appendProofIds(pm.getValidProofPool(), "valid");
1016  appendProofIds(pm.getConflictingProofPool(), "conflicting");
1017  appendProofIds(pm.getImmatureProofPool(), "immature");
1018  });
1019 
1020  return ret;
1021  },
1022  };
1023 }
1024 
1026  return RPCHelpMan{
1027  "getstakingreward",
1028  "Return the staking reward winner based on the previous block hash.\n",
1029  {
1031  "The previous block hash, hex encoded."},
1032  {"recompute", RPCArg::Type::BOOL, RPCArg::Default{false},
1033  "Whether to recompute the staking reward winner if there is a "
1034  "cached value."},
1035  },
1036  RPCResult{
1038  "payoutscript",
1039  "The winning proof payout script",
1040  {
1041  {RPCResult::Type::STR, "asm", "Decoded payout script"},
1042  {RPCResult::Type::STR_HEX, "hex",
1043  "Raw payout script in hex format"},
1044  {RPCResult::Type::STR, "type",
1045  "The output type (e.g. " + GetAllOutputTypes() + ")"},
1046  {RPCResult::Type::NUM, "reqSigs", "The required signatures"},
1048  "addresses",
1049  "",
1050  {
1051  {RPCResult::Type::STR, "address", "eCash address"},
1052  }},
1053  }},
1054  RPCExamples{HelpExampleRpc("getstakingreward", "<blockhash>")},
1055  [&](const RPCHelpMan &self, const Config &config,
1056  const JSONRPCRequest &request) -> UniValue {
1057  const NodeContext &node = EnsureAnyNodeContext(request.context);
1058  ChainstateManager &chainman = EnsureChainman(node);
1059 
1060  const BlockHash blockhash(
1061  ParseHashV(request.params[0], "blockhash"));
1062 
1063  const CBlockIndex *pprev;
1064  {
1065  LOCK(cs_main);
1066  pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1067  }
1068 
1069  if (!pprev) {
1070  throw JSONRPCError(
1072  strprintf("Block not found: %s\n", blockhash.ToString()));
1073  }
1074 
1076  config.GetChainParams().GetConsensus(), pprev)) {
1077  throw JSONRPCError(
1079  strprintf(
1080  "Staking rewards are not activated for block %s\n",
1081  blockhash.ToString()));
1082  }
1083 
1084  if (!request.params[1].isNull() && request.params[1].get_bool()) {
1085  // Force recompute the staking reward winner by first erasing
1086  // the cached entry if any
1087  g_avalanche->eraseStakingRewardWinner(blockhash);
1088  }
1089 
1090  if (!g_avalanche->computeStakingReward(pprev)) {
1091  throw JSONRPCError(
1093  strprintf("Unable to determine a staking reward winner "
1094  "for block %s\n",
1095  blockhash.ToString()));
1096  }
1097 
1098  CScript winnerPayoutScript;
1099  if (!g_avalanche->getStakingRewardWinner(blockhash,
1100  winnerPayoutScript)) {
1101  throw JSONRPCError(
1103  strprintf("Unable to retrieve the staking reward winner "
1104  "for block %s\n",
1105  blockhash.ToString()));
1106  }
1107 
1108  UniValue stakingRewardsPayoutScriptObj(UniValue::VOBJ);
1109  ScriptPubKeyToUniv(winnerPayoutScript,
1110  stakingRewardsPayoutScriptObj,
1111  /*fIncludeHex=*/true);
1112  return stakingRewardsPayoutScriptObj;
1113  },
1114  };
1115 }
1116 
1118  return RPCHelpMan{
1119  "setstakingreward",
1120  "Set the staking reward winner for the given previous block hash.\n",
1121  {
1123  "The previous block hash, hex encoded."},
1124  {"payoutscript", RPCArg::Type::STR_HEX, RPCArg::Optional::NO,
1125  "The payout script for the staking reward, hex encoded."},
1126  },
1127  RPCResult{RPCResult::Type::BOOL, "success",
1128  "Whether the payout script was set or not"},
1129  RPCExamples{
1130  HelpExampleRpc("setstakingreward", "<blockhash> <payout script>")},
1131  [&](const RPCHelpMan &self, const Config &config,
1132  const JSONRPCRequest &request) -> UniValue {
1133  const NodeContext &node = EnsureAnyNodeContext(request.context);
1134  ChainstateManager &chainman = EnsureChainman(node);
1135 
1136  const BlockHash blockhash(
1137  ParseHashV(request.params[0], "blockhash"));
1138 
1139  const CBlockIndex *pprev;
1140  {
1141  LOCK(cs_main);
1142  pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1143  }
1144 
1145  if (!pprev) {
1146  throw JSONRPCError(
1148  strprintf("Block not found: %s\n", blockhash.ToString()));
1149  }
1150 
1152  config.GetChainParams().GetConsensus(), pprev)) {
1153  throw JSONRPCError(
1155  strprintf(
1156  "Staking rewards are not activated for block %s\n",
1157  blockhash.ToString()));
1158  }
1159 
1160  const std::vector<uint8_t> data =
1161  ParseHex(request.params[1].get_str());
1162  const CScript payoutScript(data.begin(), data.end());
1163 
1164  // This will return true upon insertion or false upon replacement.
1165  // We want to convey the success of the RPC, so we always return
1166  // true.
1167  g_avalanche->setStakingRewardWinner(pprev, payoutScript);
1168  return true;
1169  },
1170  };
1171 }
1172 
1174  return RPCHelpMan{
1175  "getremoteproofs",
1176  "Get the list of remote proofs for the given node id.\n",
1177  {
1179  "The node identifier."},
1180  },
1181  RPCResult{
1183  "proofs",
1184  "",
1185  {{
1187  "proof",
1188  "",
1189  {{
1190  {RPCResult::Type::STR_HEX, "proofid",
1191  "The hex encoded proof identifier."},
1192  {RPCResult::Type::BOOL, "present",
1193  "Whether the node has the proof."},
1194  {RPCResult::Type::NUM, "last_update",
1195  "The last time this proof status was updated."},
1196  }},
1197  }},
1198  },
1199  RPCExamples{HelpExampleRpc("getremoteproofs", "<nodeid>")},
1200  [&](const RPCHelpMan &self, const Config &config,
1201  const JSONRPCRequest &request) -> UniValue {
1202  if (!g_avalanche) {
1204  "Avalanche is not initialized");
1205  }
1206 
1207  const NodeId nodeid = request.params[0].get_int64();
1208  auto remoteProofs = g_avalanche->withPeerManager(
1209  [nodeid](const avalanche::PeerManager &pm) {
1210  return pm.getRemoteProofs(nodeid);
1211  });
1212 
1213  UniValue arrOut(UniValue::VARR);
1214 
1215  for (const auto &remoteProof : remoteProofs) {
1216  UniValue obj(UniValue::VOBJ);
1217  obj.pushKV("proofid", remoteProof.proofid.ToString());
1218  obj.pushKV("present", remoteProof.present);
1219  obj.pushKV("last_update", remoteProof.lastUpdate.count());
1220 
1221  arrOut.push_back(obj);
1222  }
1223 
1224  return arrOut;
1225  },
1226  };
1227 }
1228 
1230  return RPCHelpMan{
1231  "getrawavalancheproof",
1232  "Lookup for a known avalanche proof by id.\n",
1233  {
1235  "The hex encoded avalanche proof identifier."},
1236  },
1237  RPCResult{
1239  "",
1240  "",
1241  {{
1242  {RPCResult::Type::STR_HEX, "proof",
1243  "The hex encoded proof matching the identifier."},
1244  {RPCResult::Type::BOOL, "immature",
1245  "Whether the proof has immature utxos."},
1246  {RPCResult::Type::BOOL, "boundToPeer",
1247  "Whether the proof is bound to an avalanche peer."},
1248  {RPCResult::Type::BOOL, "conflicting",
1249  "Whether the proof has a conflicting UTXO with an avalanche "
1250  "peer."},
1251  {RPCResult::Type::BOOL, "finalized",
1252  "Whether the proof is finalized by vote."},
1253  }},
1254  },
1255  RPCExamples{HelpExampleRpc("getrawavalancheproof", "<proofid>")},
1256  [&](const RPCHelpMan &self, const Config &config,
1257  const JSONRPCRequest &request) -> UniValue {
1258  if (!g_avalanche) {
1260  "Avalanche is not initialized");
1261  }
1262 
1263  const avalanche::ProofId proofid =
1264  avalanche::ProofId::fromHex(request.params[0].get_str());
1265 
1266  bool isImmature = false;
1267  bool isBoundToPeer = false;
1268  bool conflicting = false;
1269  bool finalized = false;
1270  auto proof = g_avalanche->withPeerManager(
1271  [&](const avalanche::PeerManager &pm) {
1272  isImmature = pm.isImmature(proofid);
1273  isBoundToPeer = pm.isBoundToPeer(proofid);
1274  conflicting = pm.isInConflictingPool(proofid);
1275  finalized =
1276  pm.forPeer(proofid, [&](const avalanche::Peer &p) {
1277  return p.hasFinalized;
1278  });
1279  return pm.getProof(proofid);
1280  });
1281 
1282  if (!proof) {
1283  throw JSONRPCError(RPC_INVALID_PARAMETER, "Proof not found");
1284  }
1285 
1286  UniValue ret(UniValue::VOBJ);
1287 
1289  ss << *proof;
1290  ret.pushKV("proof", HexStr(ss));
1291  ret.pushKV("immature", isImmature);
1292  ret.pushKV("boundToPeer", isBoundToPeer);
1293  ret.pushKV("conflicting", conflicting);
1294  ret.pushKV("finalized", finalized);
1295 
1296  return ret;
1297  },
1298  };
1299 }
1300 
1302  return RPCHelpMan{
1303  "invalidateavalancheproof",
1304  "Reject a known avalanche proof by id.\n",
1305  {
1307  "The hex encoded avalanche proof identifier."},
1308  },
1309  RPCResult{
1311  "success",
1312  "",
1313  },
1314  RPCExamples{HelpExampleRpc("invalidateavalancheproof", "<proofid>")},
1315  [&](const RPCHelpMan &self, const Config &config,
1316  const JSONRPCRequest &request) -> UniValue {
1317  if (!g_avalanche) {
1319  "Avalanche is not initialized");
1320  }
1321 
1322  const avalanche::ProofId proofid =
1323  avalanche::ProofId::fromHex(request.params[0].get_str());
1324 
1325  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
1326  if (!pm.exists(proofid) && !pm.isDangling(proofid)) {
1327  throw JSONRPCError(RPC_INVALID_PARAMETER,
1328  "Proof not found");
1329  }
1330 
1331  if (!pm.rejectProof(
1332  proofid,
1334  throw JSONRPCError(RPC_INTERNAL_ERROR,
1335  "Failed to reject the proof");
1336  }
1337 
1338  pm.setInvalid(proofid);
1339  });
1340 
1341  if (g_avalanche->isRecentlyFinalized(proofid)) {
1342  // If the proof was previously finalized, clear the status.
1343  // Because there is no way to selectively delete an entry from a
1344  // Bloom filter, we have to clear the whole filter which could
1345  // cause extra voting rounds.
1346  g_avalanche->clearFinalizedItems();
1347  }
1348 
1349  return true;
1350  },
1351  };
1352 }
1353 
1355  return RPCHelpMan{
1356  "isfinalblock",
1357  "Check if a block has been finalized by avalanche votes.\n",
1358  {
1360  "The hash of the block."},
1361  },
1362  RPCResult{RPCResult::Type::BOOL, "success",
1363  "Whether the block has been finalized by avalanche votes."},
1364  RPCExamples{HelpExampleRpc("isfinalblock", "<block hash>") +
1365  HelpExampleCli("isfinalblock", "<block hash>")},
1366  [&](const RPCHelpMan &self, const Config &config,
1367  const JSONRPCRequest &request) -> UniValue {
1368  if (!g_avalanche) {
1370  "Avalanche is not initialized");
1371  }
1372 
1373  if (!g_avalanche->isQuorumEstablished()) {
1375  "Avalanche is not ready to poll yet.");
1376  }
1377 
1378  ChainstateManager &chainman = EnsureAnyChainman(request.context);
1379  const BlockHash blockhash(
1380  ParseHashV(request.params[0], "blockhash"));
1381  const CBlockIndex *pindex;
1382 
1383  {
1384  LOCK(cs_main);
1385  pindex = chainman.m_blockman.LookupBlockIndex(blockhash);
1386 
1387  if (!pindex) {
1389  "Block not found");
1390  }
1391  }
1392 
1393  return chainman.ActiveChainstate().IsBlockAvalancheFinalized(
1394  pindex);
1395  },
1396  };
1397 }
1398 
1400  return RPCHelpMan{
1401  "isfinaltransaction",
1402  "Check if a transaction has been finalized by avalanche votes.\n",
1403  {
1405  "The id of the transaction."},
1407  "The block in which to look for the transaction"},
1408  },
1409  RPCResult{
1410  RPCResult::Type::BOOL, "success",
1411  "Whether the transaction has been finalized by avalanche votes."},
1412  RPCExamples{HelpExampleRpc("isfinaltransaction", "<txid> <blockhash>") +
1413  HelpExampleCli("isfinaltransaction", "<txid> <blockhash>")},
1414  [&](const RPCHelpMan &self, const Config &config,
1415  const JSONRPCRequest &request) -> UniValue {
1416  if (!g_avalanche) {
1418  "Avalanche is not initialized");
1419  }
1420 
1421  const NodeContext &node = EnsureAnyNodeContext(request.context);
1422  ChainstateManager &chainman = EnsureChainman(node);
1423  const CTxMemPool &mempool = EnsureMemPool(node);
1424  const TxId txid = TxId(ParseHashV(request.params[0], "txid"));
1425  CBlockIndex *pindex = nullptr;
1426 
1427  if (!request.params[1].isNull()) {
1428  const BlockHash blockhash(
1429  ParseHashV(request.params[1], "blockhash"));
1430 
1431  LOCK(cs_main);
1432  pindex = chainman.m_blockman.LookupBlockIndex(blockhash);
1433  if (!pindex) {
1435  "Block not found");
1436  }
1437  }
1438 
1439  bool f_txindex_ready = false;
1440  if (g_txindex && !pindex) {
1441  f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain();
1442  }
1443 
1444  BlockHash hash_block;
1445  const CTransactionRef tx = GetTransaction(
1446  pindex, &mempool, txid, config.GetChainParams().GetConsensus(),
1447  hash_block);
1448 
1449  if (!g_avalanche->isQuorumEstablished()) {
1451  "Avalanche is not ready to poll yet.");
1452  }
1453 
1454  if (!tx) {
1455  std::string errmsg;
1456  if (pindex) {
1457  if (WITH_LOCK(::cs_main,
1458  return !pindex->nStatus.hasData())) {
1460  "Block data not downloaded yet.");
1461  }
1462  errmsg = "No such transaction found in the provided block.";
1463  } else if (!g_txindex) {
1464  errmsg = "No such transaction. Use -txindex or provide a "
1465  "block hash to enable blockchain transaction "
1466  "queries.";
1467  } else if (!f_txindex_ready) {
1468  errmsg = "No such transaction. Blockchain transactions are "
1469  "still in the process of being indexed.";
1470  } else {
1471  errmsg = "No such mempool or blockchain transaction.";
1472  }
1474  }
1475 
1476  if (!pindex) {
1477  LOCK(cs_main);
1478  pindex = chainman.m_blockman.LookupBlockIndex(hash_block);
1479  }
1480 
1481  if (!tx) {
1482  // Tx not found, we should have raised an error at this stage
1483  return false;
1484  }
1485 
1486  if (mempool.isAvalancheFinalized(txid)) {
1487  // The transaction is finalized
1488  return true;
1489  }
1490 
1491  // Return true if the tx is in a finalized block
1492  return !node.mempool->exists(txid) &&
1493  chainman.ActiveChainstate().IsBlockAvalancheFinalized(
1494  pindex);
1495  },
1496  };
1497 }
1498 
1500  return RPCHelpMan{
1501  "reconsideravalancheproof",
1502  "Reconsider a known avalanche proof.\n",
1503  {
1505  "The hex encoded avalanche proof."},
1506  },
1507  RPCResult{
1509  "success",
1510  "Whether the proof has been successfully registered.",
1511  },
1512  RPCExamples{HelpExampleRpc("reconsideravalancheproof", "<proof hex>")},
1513  [&](const RPCHelpMan &self, const Config &config,
1514  const JSONRPCRequest &request) -> UniValue {
1515  if (!g_avalanche) {
1517  "Avalanche is not initialized");
1518  }
1519 
1520  auto proof = RCUPtr<avalanche::Proof>::make();
1521  NodeContext &node = EnsureAnyNodeContext(request.context);
1522 
1523  // Verify the proof. Note that this is redundant with the
1524  // verification done when adding the proof to the pool, but we get a
1525  // chance to give a better error message.
1526  verifyProofOrThrow(node, *proof, request.params[0].get_str());
1527 
1528  // There is no way to selectively clear the invalidation status of
1529  // a single proof, so we clear the whole Bloom filter. This could
1530  // cause extra voting rounds.
1531  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
1532  if (pm.isInvalid(proof->getId())) {
1533  pm.clearAllInvalid();
1534  }
1535  });
1536 
1537  // Add the proof to the pool if we don't have it already. Since the
1538  // proof verification has already been done, a failure likely
1539  // indicates that there already is a proof with conflicting utxos.
1541  if (!registerProofIfNeeded(proof, state)) {
1543  strprintf("%s (%s)\n",
1544  state.GetRejectReason(),
1545  state.GetDebugMessage()));
1546  }
1547 
1548  return g_avalanche->withPeerManager(
1549  [&](const avalanche::PeerManager &pm) {
1550  return pm.isBoundToPeer(proof->getId());
1551  });
1552  },
1553  };
1554 }
1555 
1557  return RPCHelpMan{
1558  "sendavalancheproof",
1559  "Broadcast an avalanche proof.\n",
1560  {
1562  "The avalanche proof to broadcast."},
1563  },
1564  RPCResult{RPCResult::Type::BOOL, "success",
1565  "Whether the proof was sent successfully or not."},
1566  RPCExamples{HelpExampleRpc("sendavalancheproof", "<proof>")},
1567  [&](const RPCHelpMan &self, const Config &config,
1568  const JSONRPCRequest &request) -> UniValue {
1569  if (!g_avalanche) {
1571  "Avalanche is not initialized");
1572  }
1573 
1574  auto proof = RCUPtr<avalanche::Proof>::make();
1575  NodeContext &node = EnsureAnyNodeContext(request.context);
1576 
1577  // Verify the proof. Note that this is redundant with the
1578  // verification done when adding the proof to the pool, but we get a
1579  // chance to give a better error message.
1580  verifyProofOrThrow(node, *proof, request.params[0].get_str());
1581 
1582  // Add the proof to the pool if we don't have it already. Since the
1583  // proof verification has already been done, a failure likely
1584  // indicates that there already is a proof with conflicting utxos.
1585  const avalanche::ProofId &proofid = proof->getId();
1587  if (!registerProofIfNeeded(proof, state)) {
1589  strprintf("%s (%s)\n",
1590  state.GetRejectReason(),
1591  state.GetDebugMessage()));
1592  }
1593 
1594  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
1595  pm.addUnbroadcastProof(proofid);
1596  });
1597 
1598  if (node.peerman) {
1599  node.peerman->RelayProof(proofid);
1600  }
1601 
1602  return true;
1603  },
1604  };
1605 }
1606 
1608  return RPCHelpMan{
1609  "verifyavalancheproof",
1610  "Verify an avalanche proof is valid and return the error otherwise.\n",
1611  {
1613  "Proof to verify."},
1614  },
1615  RPCResult{RPCResult::Type::BOOL, "success",
1616  "Whether the proof is valid or not."},
1617  RPCExamples{HelpExampleRpc("verifyavalancheproof", "\"<proof>\"")},
1618  [&](const RPCHelpMan &self, const Config &config,
1619  const JSONRPCRequest &request) -> UniValue {
1620  RPCTypeCheck(request.params, {UniValue::VSTR});
1621 
1622  avalanche::Proof proof;
1623  verifyProofOrThrow(EnsureAnyNodeContext(request.context), proof,
1624  request.params[0].get_str());
1625 
1626  return true;
1627  },
1628  };
1629 }
1630 
1632  return RPCHelpMan{
1633  "verifyavalanchedelegation",
1634  "Verify an avalanche delegation is valid and return the error "
1635  "otherwise.\n",
1636  {
1638  "The avalanche proof delegation to verify."},
1639  },
1640  RPCResult{RPCResult::Type::BOOL, "success",
1641  "Whether the delegation is valid or not."},
1642  RPCExamples{HelpExampleRpc("verifyavalanchedelegation", "\"<proof>\"")},
1643  [&](const RPCHelpMan &self, const Config &config,
1644  const JSONRPCRequest &request) -> UniValue {
1645  RPCTypeCheck(request.params, {UniValue::VSTR});
1646 
1647  avalanche::Delegation delegation;
1648  CPubKey dummy;
1649  verifyDelegationOrThrow(delegation, request.params[0].get_str(),
1650  dummy);
1651 
1652  return true;
1653  },
1654  };
1655 }
1656 
1658  // clang-format off
1659  static const CRPCCommand commands[] = {
1660  // category actor (function)
1661  // ----------------- --------------------
1662  { "avalanche", getavalanchekey, },
1663  { "avalanche", addavalanchenode, },
1664  { "avalanche", buildavalancheproof, },
1665  { "avalanche", decodeavalancheproof, },
1666  { "avalanche", delegateavalancheproof, },
1667  { "avalanche", decodeavalanchedelegation, },
1668  { "avalanche", getavalancheinfo, },
1669  { "avalanche", getavalanchepeerinfo, },
1670  { "avalanche", getavalancheproofs, },
1671  { "avalanche", getstakingreward, },
1672  { "avalanche", setstakingreward, },
1673  { "avalanche", getremoteproofs, },
1674  { "avalanche", getrawavalancheproof, },
1675  { "avalanche", invalidateavalancheproof, },
1676  { "avalanche", isfinalblock, },
1677  { "avalanche", isfinaltransaction, },
1678  { "avalanche", reconsideravalancheproof, },
1679  { "avalanche", sendavalancheproof, },
1680  { "avalanche", verifyavalancheproof, },
1681  { "avalanche", verifyavalanchedelegation, },
1682  };
1683  // clang-format on
1684 
1685  for (const auto &c : commands) {
1686  t.appendCommand(c.name, &c);
1687  }
1688 }
std::unique_ptr< avalanche::Processor > g_avalanche
Global avalanche instance.
Definition: processor.cpp:38
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:111
#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:27
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:199
An encapsulated secp256k1 private key.
Definition: key.h:28
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:94
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:210
Information about a peer.
Definition: net.h:456
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
uint32_t GetN() const
Definition: transaction.h:36
const TxId & GetTxId() const
Definition: transaction.h:35
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:183
void appendCommand(const std::string &name, const CRPCCommand *pcmd)
Appends a CRPCCommand to the dispatch table.
Definition: server.cpp:330
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:431
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:354
bool isAvalancheFinalized(const TxId &txid) const
Definition: txmempool.h:638
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1166
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1285
Definition: config.h:17
static RCUPtr make(Args &&...args)
Construct a new object that is owned by the pointer.
Definition: rcu.h:112
const std::string & get_str() const
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:234
@ VOBJ
Definition: univalue.h:27
@ VSTR
Definition: univalue.h:27
@ VARR
Definition: univalue.h:27
@ VNUM
Definition: univalue.h:27
bool isNull() const
Definition: univalue.h:89
size_t size() const
Definition: univalue.h:80
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
const UniValue & get_array() const
bool exists(const std::string &key) const
Definition: univalue.h:87
bool pushKV(const std::string &key, const UniValue &val)
Definition: univalue.cpp:133
bool get_bool() const
int get_int() const
bool IsValid() const
Definition: validation.h:112
std::string GetRejectReason() const
Definition: validation.h:116
std::string GetDebugMessage() const
Definition: validation.h:117
std::string ToString() const
Definition: validation.h:118
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 LimitedProofId & getLimitedProofId() const
Definition: delegation.h:61
const CPubKey & getDelegatedPubkey() const
Definition: delegation.cpp:60
const DelegationId & getId() const
Definition: delegation.h:60
const CPubKey & getProofMaster() const
Definition: delegation.h:62
std::vector< RemoteProof > getRemoteProofs(const NodeId nodeid) const
bool isDangling(const ProofId &proofid) const
bool exists(const ProofId &proofid) const
Definition: peermanager.h:400
bool forPeer(const ProofId &proofid, Callable &&func) const
Definition: peermanager.h:408
bool addNode(NodeId nodeid, const ProofId &proofid)
Node API.
Definition: peermanager.cpp:29
void addUnbroadcastProof(const ProofId &proofid)
Proof broadcast API.
bool isBoundToPeer(const ProofId &proofid) const
const ProofPool & getConflictingProofPool() const
Definition: peermanager.h:491
size_t getPendingNodeCount() const
Definition: peermanager.h:312
void forEachPeer(Callable &&func) const
Definition: peermanager.h:414
const ProofPool & getValidProofPool() const
Definition: peermanager.h:490
void setInvalid(const ProofId &proofid)
void forEachNode(const Peer &peer, Callable &&func) const
Definition: peermanager.h:338
const Amount & getStakeUtxoDustThreshold() const
Definition: peermanager.h:510
bool isInvalid(const ProofId &proofid) const
const ProofPool & getImmatureProofPool() const
Definition: peermanager.h:494
bool isImmature(const ProofId &proofid) const
bool rejectProof(const ProofId &proofid, RejectionMode mode=RejectionMode::DEFAULT)
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
const ProofId & getId() const
Definition: proof.h:169
const std::vector< SignedStake > & getStakes() const
Definition: proof.h:165
bool verify(const Amount &stakeUtxoDustThreshold, ProofValidationState &state) const
Definition: proof.cpp:111
Amount getStakedAmount() const
Definition: proof.cpp:104
uint64_t getSequence() const
Definition: proof.h:162
const CScript & getPayoutScript() const
Definition: proof.h:166
const CPubKey & getMaster() const
Definition: proof.h:164
const SchnorrSig & getSignature() const
Definition: proof.h:167
uint32_t getScore() const
Definition: proof.h:174
const LimitedProofId & getLimitedId() const
Definition: proof.h:170
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:78
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
void ScriptPubKeyToUniv(const CScript &scriptPubKey, UniValue &out, bool fIncludeHex)
Definition: core_write.cpp:190
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
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, const Consensus::Params &consensusParams, BlockHash &hashBlock)
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:57
static RPCHelpMan buildavalancheproof()
Definition: avalanche.cpp:213
static RPCHelpMan invalidateavalancheproof()
Definition: avalanche.cpp:1301
static RPCHelpMan delegateavalancheproof()
Definition: avalanche.cpp:474
static RPCHelpMan getremoteproofs()
Definition: avalanche.cpp:1173
static RPCHelpMan decodeavalanchedelegation()
Definition: avalanche.cpp:554
static RPCHelpMan sendavalancheproof()
Definition: avalanche.cpp:1556
static RPCHelpMan getavalancheproofs()
Definition: avalanche.cpp:961
static void verifyDelegationOrThrow(avalanche::Delegation &dg, const std::string &dgHex, CPubKey &auth)
Definition: avalanche.cpp:81
static RPCHelpMan getrawavalancheproof()
Definition: avalanche.cpp:1229
static void verifyProofOrThrow(const NodeContext &node, avalanche::Proof &proof, const std::string &proofHex)
Definition: avalanche.cpp:95
void RegisterAvalancheRPCCommands(CRPCTable &t)
Definition: avalanche.cpp:1657
static RPCHelpMan getavalanchekey()
Definition: avalanche.cpp:32
static RPCHelpMan addavalanchenode()
Definition: avalanche.cpp:121
static bool registerProofIfNeeded(avalanche::ProofRef proof, avalanche::ProofRegistrationState &state)
Definition: avalanche.cpp:63
static CPubKey ParsePubKey(const UniValue &param)
Definition: avalanche.cpp:51
static RPCHelpMan verifyavalanchedelegation()
Definition: avalanche.cpp:1631
static RPCHelpMan setstakingreward()
Definition: avalanche.cpp:1117
static RPCHelpMan isfinalblock()
Definition: avalanche.cpp:1354
static RPCHelpMan reconsideravalancheproof()
Definition: avalanche.cpp:1499
static RPCHelpMan isfinaltransaction()
Definition: avalanche.cpp:1399
static RPCHelpMan getstakingreward()
Definition: avalanche.cpp:1025
static RPCHelpMan getavalanchepeerinfo()
Definition: avalanche.cpp:859
static RPCHelpMan verifyavalancheproof()
Definition: avalanche.cpp:1607
static RPCHelpMan getavalancheinfo()
Definition: avalanche.cpp:639
static RPCHelpMan decodeavalancheproof()
Definition: avalanche.cpp:347
@ 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
void RPCTypeCheck(const UniValue &params, const std::list< UniValueType > &typesExpected, bool fAllowNull)
Type-check arguments; throws JSONRPCError if wrong type given.
Definition: util.cpp:24
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:175
Amount AmountFromValue(const UniValue &value)
Definition: util.cpp:80
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:192
std::string GetAllOutputTypes()
Definition: util.cpp:330
CPubKey HexToPubKey(const std::string &hex_in)
Definition: util.cpp:216
uint256 ParseHashO(const UniValue &o, std::string strKey)
Definition: util.cpp:115
uint256 ParseHashV(const UniValue &v, std::string strName)
Utilities: convert hex-encoded values (throws error if not hex).
Definition: util.cpp:98
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:51
@ SER_NETWORK
Definition: serialize.h:166
bool IsDeprecatedRPCEnabled(const ArgsManager &args, const std::string &method)
Definition: server.cpp:410
ChainstateManager & EnsureAnyChainman(const std::any &context)
Definition: server_util.cpp:46
NodeContext & EnsureAnyNodeContext(const std::any &context)
Definition: server_util.cpp:19
CTxMemPool & EnsureMemPool(const NodeContext &node)
Definition: server_util.cpp:27
ChainstateManager & EnsureChainman(const NodeContext &node)
Definition: server_util.cpp:39
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:161
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:263
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:243
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:92
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)
bool IsHex(const std::string &str)
Returns true if each character in str is a hex character, and has an even number of hex digits.
std::vector< uint8_t > ParseHex(const char *psz)
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
Optional argument with default value omitted because they are implicitly clear.
@ 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:38
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
ArgsManager gArgs
Definition: system.cpp:79
bool error(const char *fmt, const Args &...args)
Definition: system.h:45
#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:17
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11