Bitcoin ABC  0.29.2
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::STR_HEX, "proofid",
663  "The node local proof id."},
664  {RPCResult::Type::STR_HEX, "limited_proofid",
665  "The node local limited proof id."},
666  {RPCResult::Type::STR_HEX, "master",
667  "The node local proof master public key."},
668  {RPCResult::Type::STR, "payout_address",
669  "The node local proof payout address. This might be "
670  "omitted if the payout script is not one of P2PK, P2PKH "
671  "or P2SH, in which case decodeavalancheproof can be used "
672  "to get more details."},
673  {RPCResult::Type::STR_AMOUNT, "stake_amount",
674  "The node local proof staked amount."},
675  }},
677  "network",
678  "",
679  {
680  {RPCResult::Type::NUM, "proof_count",
681  "The number of valid avalanche proofs we know exist "
682  "(including this node's local proof if applicable)."},
683  {RPCResult::Type::NUM, "connected_proof_count",
684  "The number of avalanche proofs with at least one node "
685  "we are connected to (including this node's local proof "
686  "if applicable)."},
687  {RPCResult::Type::NUM, "dangling_proof_count",
688  "The number of avalanche proofs with no node attached."},
689  {RPCResult::Type::NUM, "finalized_proof_count",
690  "The number of known avalanche proofs that have been "
691  "finalized by avalanche."},
692  {RPCResult::Type::NUM, "conflicting_proof_count",
693  "The number of known avalanche proofs that conflict with "
694  "valid proofs."},
695  {RPCResult::Type::NUM, "immature_proof_count",
696  "The number of known avalanche proofs that have immature "
697  "utxos."},
698  {RPCResult::Type::STR_AMOUNT, "total_stake_amount",
699  "The total staked amount over all the valid proofs in " +
701  " (including this node's local proof if "
702  "applicable)."},
703  {RPCResult::Type::STR_AMOUNT, "connected_stake_amount",
704  "The total staked amount over all the connected proofs "
705  "in " +
707  " (including this node's local proof if "
708  "applicable)."},
709  {RPCResult::Type::STR_AMOUNT, "dangling_stake_amount",
710  "The total staked amount over all the dangling proofs "
711  "in " +
713  " (including this node's local proof if "
714  "applicable)."},
715  {RPCResult::Type::STR_AMOUNT, "immature_stake_amount",
716  "The total staked amount over all the immature proofs "
717  "in " +
719  " (including this node's local proof if "
720  "applicable)."},
721  {RPCResult::Type::NUM, "node_count",
722  "The number of avalanche nodes we are connected to "
723  "(including this node if a local proof is set)."},
724  {RPCResult::Type::NUM, "connected_node_count",
725  "The number of avalanche nodes associated with an "
726  "avalanche proof (including this node if a local proof "
727  "is set)."},
728  {RPCResult::Type::NUM, "pending_node_count",
729  "The number of avalanche nodes pending for a proof."},
730  }},
731  },
732  },
733  RPCExamples{HelpExampleCli("getavalancheinfo", "") +
734  HelpExampleRpc("getavalancheinfo", "")},
735  [&](const RPCHelpMan &self, const Config &config,
736  const JSONRPCRequest &request) -> UniValue {
737  if (!g_avalanche) {
739  "Avalanche is not initialized");
740  }
741 
743  ret.pushKV("ready_to_poll", g_avalanche->isQuorumEstablished());
744 
745  auto localProof = g_avalanche->getLocalProof();
746  if (localProof != nullptr) {
747  UniValue local(UniValue::VOBJ);
748  const bool verified = g_avalanche->withPeerManager(
749  [&](const avalanche::PeerManager &pm) {
750  const avalanche::ProofId &proofid = localProof->getId();
751  return pm.isBoundToPeer(proofid);
752  });
753  local.pushKV("verified", verified);
754  const bool sharing = g_avalanche->canShareLocalProof();
755  if (!verified) {
757  g_avalanche->getLocalProofRegistrationState();
758  // If the local proof is not registered but the state is
759  // valid, no registration attempt occurred yet.
760  local.pushKV("verification_status",
761  state.IsValid()
762  ? (sharing ? "pending verification"
763  : "pending inbound connections")
764  : state.GetRejectReason());
765  }
766  local.pushKV("proofid", localProof->getId().ToString());
767  local.pushKV("limited_proofid",
768  localProof->getLimitedId().ToString());
769  local.pushKV("master", HexStr(localProof->getMaster()));
770  CTxDestination destination;
771  if (ExtractDestination(localProof->getPayoutScript(),
772  destination)) {
773  local.pushKV("payout_address",
774  EncodeDestination(destination, config));
775  }
776  local.pushKV("stake_amount", localProof->getStakedAmount());
777  ret.pushKV("local", local);
778  }
779 
780  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
781  UniValue network(UniValue::VOBJ);
782 
783  uint64_t proofCount{0};
784  uint64_t connectedProofCount{0};
785  uint64_t finalizedProofCount{0};
786  uint64_t connectedNodeCount{0};
787  Amount totalStakes = Amount::zero();
788  Amount connectedStakes = Amount::zero();
789 
790  pm.forEachPeer([&](const avalanche::Peer &peer) {
791  CHECK_NONFATAL(peer.proof != nullptr);
792 
793  const bool isLocalProof =
794  localProof &&
795  peer.proof->getId() == localProof->getId();
796 
797  ++proofCount;
798  const Amount proofStake = peer.proof->getStakedAmount();
799 
800  totalStakes += proofStake;
801 
802  if (peer.hasFinalized) {
803  ++finalizedProofCount;
804  }
805 
806  if (peer.node_count > 0 || isLocalProof) {
807  ++connectedProofCount;
808  connectedStakes += proofStake;
809  }
810 
811  connectedNodeCount += peer.node_count + isLocalProof;
812  });
813 
814  Amount immatureStakes = Amount::zero();
816  [&](const avalanche::ProofRef &proof) {
817  immatureStakes += proof->getStakedAmount();
818  });
819 
820  network.pushKV("proof_count", proofCount);
821  network.pushKV("connected_proof_count", connectedProofCount);
822  network.pushKV("dangling_proof_count",
823  proofCount - connectedProofCount);
824 
825  network.pushKV("finalized_proof_count", finalizedProofCount);
826  network.pushKV(
827  "conflicting_proof_count",
828  uint64_t(pm.getConflictingProofPool().countProofs()));
829  network.pushKV(
830  "immature_proof_count",
831  uint64_t(pm.getImmatureProofPool().countProofs()));
832 
833  network.pushKV("total_stake_amount", totalStakes);
834  network.pushKV("connected_stake_amount", connectedStakes);
835  network.pushKV("dangling_stake_amount",
836  totalStakes - connectedStakes);
837  network.pushKV("immature_stake_amount", immatureStakes);
838 
839  const uint64_t pendingNodes = pm.getPendingNodeCount();
840  network.pushKV("node_count", connectedNodeCount + pendingNodes);
841  network.pushKV("connected_node_count", connectedNodeCount);
842  network.pushKV("pending_node_count", pendingNodes);
843 
844  ret.pushKV("network", network);
845  });
846 
847  return ret;
848  },
849  };
850 }
851 
853  return RPCHelpMan{
854  "getavalanchepeerinfo",
855  "Returns data about an avalanche peer as a json array of objects. If "
856  "no proofid is provided, returns data about all the peers.\n",
857  {
859  "The hex encoded avalanche proof identifier."},
860  },
861  RPCResult{
863  "",
864  "",
865  {{
867  "",
868  "",
869  {{
870  {RPCResult::Type::NUM, "avalanche_peerid",
871  "The avalanche internal peer identifier"},
872  {RPCResult::Type::NUM, "availability_score",
873  "The agreggated availability score of this peer's nodes"},
874  {RPCResult::Type::STR_HEX, "proofid",
875  "The avalanche proof id used by this peer"},
876  {RPCResult::Type::STR_HEX, "proof",
877  "The avalanche proof used by this peer"},
878  {RPCResult::Type::NUM, "nodecount",
879  "The number of nodes for this peer"},
881  "node_list",
882  "",
883  {
884  {RPCResult::Type::NUM, "nodeid",
885  "Node id, as returned by getpeerinfo"},
886  }},
887  }},
888  }},
889  },
890  RPCExamples{HelpExampleCli("getavalanchepeerinfo", "") +
891  HelpExampleCli("getavalanchepeerinfo", "\"proofid\"") +
892  HelpExampleRpc("getavalanchepeerinfo", "") +
893  HelpExampleRpc("getavalanchepeerinfo", "\"proofid\"")},
894  [&](const RPCHelpMan &self, const Config &config,
895  const JSONRPCRequest &request) -> UniValue {
896  RPCTypeCheck(request.params, {UniValue::VSTR});
897 
898  if (!g_avalanche) {
900  "Avalanche is not initialized");
901  }
902 
903  auto peerToUniv = [](const avalanche::PeerManager &pm,
904  const avalanche::Peer &peer) {
906 
907  obj.pushKV("avalanche_peerid", uint64_t(peer.peerid));
908  obj.pushKV("availability_score", peer.availabilityScore);
909  obj.pushKV("proofid", peer.getProofId().ToString());
910  obj.pushKV("proof", peer.proof->ToHex());
911 
912  UniValue nodes(UniValue::VARR);
913  pm.forEachNode(peer, [&](const avalanche::Node &n) {
914  nodes.push_back(n.nodeid);
915  });
916 
917  obj.pushKV("nodecount", uint64_t(peer.node_count));
918  obj.pushKV("node_list", nodes);
919 
920  return obj;
921  };
922 
924 
925  g_avalanche->withPeerManager([&](const avalanche::PeerManager &pm) {
926  // If a proofid is provided, only return the associated peer
927  if (!request.params[0].isNull()) {
928  const avalanche::ProofId proofid =
929  avalanche::ProofId::fromHex(
930  request.params[0].get_str());
931  if (!pm.isBoundToPeer(proofid)) {
932  throw JSONRPCError(RPC_INVALID_PARAMETER,
933  "Proofid not found");
934  }
935 
936  pm.forPeer(proofid, [&](const avalanche::Peer &peer) {
937  return ret.push_back(peerToUniv(pm, peer));
938  });
939 
940  return;
941  }
942 
943  // If no proofid is provided, return all the peers
944  pm.forEachPeer([&](const avalanche::Peer &peer) {
945  ret.push_back(peerToUniv(pm, peer));
946  });
947  });
948 
949  return ret;
950  },
951  };
952 }
953 
955  return RPCHelpMan{
956  "getavalancheproofs",
957  "Returns an object containing all tracked proofids.\n",
958  {},
959  RPCResult{
961  "",
962  "",
963  {
965  "valid",
966  "",
967  {
968  {RPCResult::Type::STR_HEX, "proofid",
969  "Avalanche proof id"},
970  }},
972  "conflicting",
973  "",
974  {
975  {RPCResult::Type::STR_HEX, "proofid",
976  "Avalanche proof id"},
977  }},
979  "immature",
980  "",
981  {
982  {RPCResult::Type::STR_HEX, "proofid",
983  "Avalanche proof id"},
984  }},
985  },
986  },
987  RPCExamples{HelpExampleCli("getavalancheproofs", "") +
988  HelpExampleRpc("getavalancheproofs", "")},
989  [&](const RPCHelpMan &self, const Config &config,
990  const JSONRPCRequest &request) -> UniValue {
991  if (!g_avalanche) {
993  "Avalanche is not initialized");
994  }
995 
997  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
998  auto appendProofIds = [&ret](const avalanche::ProofPool &pool,
999  const std::string &key) {
1000  UniValue arrOut(UniValue::VARR);
1001  for (const avalanche::ProofId &proofid :
1002  pool.getProofIds()) {
1003  arrOut.push_back(proofid.ToString());
1004  }
1005  ret.pushKV(key, arrOut);
1006  };
1007 
1008  appendProofIds(pm.getValidProofPool(), "valid");
1009  appendProofIds(pm.getConflictingProofPool(), "conflicting");
1010  appendProofIds(pm.getImmatureProofPool(), "immature");
1011  });
1012 
1013  return ret;
1014  },
1015  };
1016 }
1017 
1019  return RPCHelpMan{
1020  "getstakingreward",
1021  "Return a list of possible staking reward winners based on the previous "
1022  "block hash.\n"
1023  "If -deprecatedrpc=getstakingreward is set it returns a single payout "
1024  "script instead of an array.\n",
1025  {
1027  "The previous block hash, hex encoded."},
1028  {"recompute", RPCArg::Type::BOOL, RPCArg::Default{false},
1029  "Whether to recompute the staking reward winner if there is a "
1030  "cached value."},
1031  },
1032  // Deprecated in 0.28.11
1033  IsDeprecatedRPCEnabled(gArgs, "getstakingreward")?
1034  RPCResult{
1036  "payoutscript",
1037  "The winning proof payout script",
1038  {
1039  {RPCResult::Type::STR, "asm", "Decoded payout script"},
1040  {RPCResult::Type::STR_HEX, "hex",
1041  "Raw payout script in hex format"},
1042  {RPCResult::Type::STR, "type",
1043  "The output type (e.g. " + GetAllOutputTypes() + ")"},
1044  {RPCResult::Type::NUM, "reqSigs",
1045  "The required signatures"},
1047  "addresses",
1048  "",
1049  {
1050  {RPCResult::Type::STR, "address", "eCash address"},
1051  }},
1052  },
1053  }
1054  :
1055  RPCResult{
1057  "",
1058  "",
1059  {
1061  "payoutscript",
1062  "The winning proof payout script",
1063  {
1064  {RPCResult::Type::STR, "asm", "Decoded payout script"},
1065  {RPCResult::Type::STR_HEX, "hex",
1066  "Raw payout script in hex format"},
1067  {RPCResult::Type::STR, "type",
1068  "The output type (e.g. " + GetAllOutputTypes() + ")"},
1069  {RPCResult::Type::NUM, "reqSigs",
1070  "The required signatures"},
1072  "addresses",
1073  "",
1074  {
1075  {RPCResult::Type::STR, "address", "eCash address"},
1076  }},
1077  }},
1078  }},
1079  RPCExamples{HelpExampleRpc("getstakingreward", "<blockhash>")},
1080  [&](const RPCHelpMan &self, const Config &config,
1081  const JSONRPCRequest &request) -> UniValue {
1082  const NodeContext &node = EnsureAnyNodeContext(request.context);
1083  ChainstateManager &chainman = EnsureChainman(node);
1084  const ArgsManager &args{EnsureAnyArgsman(request.context)};
1085 
1086  const BlockHash blockhash(
1087  ParseHashV(request.params[0], "blockhash"));
1088 
1089  const CBlockIndex *pprev;
1090  {
1091  LOCK(cs_main);
1092  pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1093  }
1094 
1095  if (!pprev) {
1096  throw JSONRPCError(
1098  strprintf("Block not found: %s\n", blockhash.ToString()));
1099  }
1100 
1102  config.GetChainParams().GetConsensus(), pprev)) {
1103  throw JSONRPCError(
1105  strprintf(
1106  "Staking rewards are not activated for block %s\n",
1107  blockhash.ToString()));
1108  }
1109 
1110  if (!request.params[1].isNull() && request.params[1].get_bool()) {
1111  // Force recompute the staking reward winner by first erasing
1112  // the cached entry if any
1113  g_avalanche->eraseStakingRewardWinner(blockhash);
1114  }
1115 
1116  if (!g_avalanche->computeStakingReward(pprev)) {
1117  throw JSONRPCError(
1119  strprintf("Unable to determine a staking reward winner "
1120  "for block %s\n",
1121  blockhash.ToString()));
1122  }
1123 
1124  std::vector<CScript> winnerPayoutScripts;
1125  if (!g_avalanche->getStakingRewardWinners(blockhash,
1126  winnerPayoutScripts)) {
1127  throw JSONRPCError(
1129  strprintf("Unable to retrieve the staking reward winner "
1130  "for block %s\n",
1131  blockhash.ToString()));
1132  }
1133 
1134  UniValue winners(UniValue::VARR);
1135  for (auto &winnerPayoutScript : winnerPayoutScripts) {
1136  UniValue stakingRewardsPayoutScriptObj(UniValue::VOBJ);
1137  ScriptPubKeyToUniv(winnerPayoutScript,
1138  stakingRewardsPayoutScriptObj,
1139  /*fIncludeHex=*/true);
1140  if (IsDeprecatedRPCEnabled(args, "getstakingreward")) {
1141  return stakingRewardsPayoutScriptObj;
1142  }
1143  winners.push_back(stakingRewardsPayoutScriptObj);
1144  }
1145 
1146  return winners;
1147  },
1148  };
1149 }
1150 
1152  return RPCHelpMan{
1153  "setstakingreward",
1154  "Set the staking reward winner for the given previous block hash.\n",
1155  {
1157  "The previous block hash, hex encoded."},
1158  {"payoutscript", RPCArg::Type::STR_HEX, RPCArg::Optional::NO,
1159  "The payout script for the staking reward, hex encoded."},
1160  {"append", RPCArg::Type::BOOL, RPCArg::Default{false},
1161  "Append to the list of possible winners instead of replacing."},
1162  },
1163  RPCResult{RPCResult::Type::BOOL, "success",
1164  "Whether the payout script was set or not"},
1165  RPCExamples{
1166  HelpExampleRpc("setstakingreward", "<blockhash> <payout script>")},
1167  [&](const RPCHelpMan &self, const Config &config,
1168  const JSONRPCRequest &request) -> UniValue {
1169  const NodeContext &node = EnsureAnyNodeContext(request.context);
1170  ChainstateManager &chainman = EnsureChainman(node);
1171 
1172  const BlockHash blockhash(
1173  ParseHashV(request.params[0], "blockhash"));
1174 
1175  const CBlockIndex *pprev;
1176  {
1177  LOCK(cs_main);
1178  pprev = chainman.m_blockman.LookupBlockIndex(blockhash);
1179  }
1180 
1181  if (!pprev) {
1182  throw JSONRPCError(
1184  strprintf("Block not found: %s\n", blockhash.ToString()));
1185  }
1186 
1188  config.GetChainParams().GetConsensus(), pprev)) {
1189  throw JSONRPCError(
1191  strprintf(
1192  "Staking rewards are not activated for block %s\n",
1193  blockhash.ToString()));
1194  }
1195 
1196  const std::vector<uint8_t> data =
1197  ParseHex(request.params[1].get_str());
1198  const CScript payoutScript(data.begin(), data.end());
1199 
1200  std::vector<CScript> payoutScripts;
1201 
1202  if (!request.params[2].isNull() && request.params[2].get_bool()) {
1203  // Append mode, initialize our list with the current winners
1204  // and the new one will be added to the back of that list. If
1205  // there is no winner the list will remain empty.
1206  g_avalanche->getStakingRewardWinners(blockhash, payoutScripts);
1207  }
1208 
1209  payoutScripts.push_back(std::move(payoutScript));
1210 
1211  // This will return true upon insertion or false upon replacement.
1212  // We want to convey the success of the RPC, so we always return
1213  // true.
1214  g_avalanche->setStakingRewardWinners(pprev, payoutScripts);
1215  return true;
1216  },
1217  };
1218 }
1219 
1221  return RPCHelpMan{
1222  "getremoteproofs",
1223  "Get the list of remote proofs for the given node id.\n",
1224  {
1226  "The node identifier."},
1227  },
1228  RPCResult{
1230  "proofs",
1231  "",
1232  {{
1234  "proof",
1235  "",
1236  {{
1237  {RPCResult::Type::STR_HEX, "proofid",
1238  "The hex encoded proof identifier."},
1239  {RPCResult::Type::BOOL, "present",
1240  "Whether the node has the proof."},
1241  {RPCResult::Type::NUM, "last_update",
1242  "The last time this proof status was updated."},
1243  }},
1244  }},
1245  },
1246  RPCExamples{HelpExampleRpc("getremoteproofs", "<nodeid>")},
1247  [&](const RPCHelpMan &self, const Config &config,
1248  const JSONRPCRequest &request) -> UniValue {
1249  if (!g_avalanche) {
1251  "Avalanche is not initialized");
1252  }
1253 
1254  const NodeId nodeid = request.params[0].get_int64();
1255  auto remoteProofs = g_avalanche->withPeerManager(
1256  [nodeid](const avalanche::PeerManager &pm) {
1257  return pm.getRemoteProofs(nodeid);
1258  });
1259 
1260  UniValue arrOut(UniValue::VARR);
1261 
1262  for (const auto &remoteProof : remoteProofs) {
1263  UniValue obj(UniValue::VOBJ);
1264  obj.pushKV("proofid", remoteProof.proofid.ToString());
1265  obj.pushKV("present", remoteProof.present);
1266  obj.pushKV("last_update", remoteProof.lastUpdate.count());
1267 
1268  arrOut.push_back(obj);
1269  }
1270 
1271  return arrOut;
1272  },
1273  };
1274 }
1275 
1277  return RPCHelpMan{
1278  "getrawavalancheproof",
1279  "Lookup for a known avalanche proof by id.\n",
1280  {
1282  "The hex encoded avalanche proof identifier."},
1283  },
1284  RPCResult{
1286  "",
1287  "",
1288  {{
1289  {RPCResult::Type::STR_HEX, "proof",
1290  "The hex encoded proof matching the identifier."},
1291  {RPCResult::Type::BOOL, "immature",
1292  "Whether the proof has immature utxos."},
1293  {RPCResult::Type::BOOL, "boundToPeer",
1294  "Whether the proof is bound to an avalanche peer."},
1295  {RPCResult::Type::BOOL, "conflicting",
1296  "Whether the proof has a conflicting UTXO with an avalanche "
1297  "peer."},
1298  {RPCResult::Type::BOOL, "finalized",
1299  "Whether the proof is finalized by vote."},
1300  }},
1301  },
1302  RPCExamples{HelpExampleRpc("getrawavalancheproof", "<proofid>")},
1303  [&](const RPCHelpMan &self, const Config &config,
1304  const JSONRPCRequest &request) -> UniValue {
1305  if (!g_avalanche) {
1307  "Avalanche is not initialized");
1308  }
1309 
1310  const avalanche::ProofId proofid =
1311  avalanche::ProofId::fromHex(request.params[0].get_str());
1312 
1313  bool isImmature = false;
1314  bool isBoundToPeer = false;
1315  bool conflicting = false;
1316  bool finalized = false;
1317  auto proof = g_avalanche->withPeerManager(
1318  [&](const avalanche::PeerManager &pm) {
1319  isImmature = pm.isImmature(proofid);
1320  isBoundToPeer = pm.isBoundToPeer(proofid);
1321  conflicting = pm.isInConflictingPool(proofid);
1322  finalized =
1323  pm.forPeer(proofid, [&](const avalanche::Peer &p) {
1324  return p.hasFinalized;
1325  });
1326  return pm.getProof(proofid);
1327  });
1328 
1329  if (!proof) {
1330  throw JSONRPCError(RPC_INVALID_PARAMETER, "Proof not found");
1331  }
1332 
1333  UniValue ret(UniValue::VOBJ);
1334 
1336  ss << *proof;
1337  ret.pushKV("proof", HexStr(ss));
1338  ret.pushKV("immature", isImmature);
1339  ret.pushKV("boundToPeer", isBoundToPeer);
1340  ret.pushKV("conflicting", conflicting);
1341  ret.pushKV("finalized", finalized);
1342 
1343  return ret;
1344  },
1345  };
1346 }
1347 
1349  return RPCHelpMan{
1350  "invalidateavalancheproof",
1351  "Reject a known avalanche proof by id.\n",
1352  {
1354  "The hex encoded avalanche proof identifier."},
1355  },
1356  RPCResult{
1358  "success",
1359  "",
1360  },
1361  RPCExamples{HelpExampleRpc("invalidateavalancheproof", "<proofid>")},
1362  [&](const RPCHelpMan &self, const Config &config,
1363  const JSONRPCRequest &request) -> UniValue {
1364  if (!g_avalanche) {
1366  "Avalanche is not initialized");
1367  }
1368 
1369  const avalanche::ProofId proofid =
1370  avalanche::ProofId::fromHex(request.params[0].get_str());
1371 
1372  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
1373  if (!pm.exists(proofid) && !pm.isDangling(proofid)) {
1374  throw JSONRPCError(RPC_INVALID_PARAMETER,
1375  "Proof not found");
1376  }
1377 
1378  if (!pm.rejectProof(
1379  proofid,
1381  throw JSONRPCError(RPC_INTERNAL_ERROR,
1382  "Failed to reject the proof");
1383  }
1384 
1385  pm.setInvalid(proofid);
1386  });
1387 
1388  if (g_avalanche->isRecentlyFinalized(proofid)) {
1389  // If the proof was previously finalized, clear the status.
1390  // Because there is no way to selectively delete an entry from a
1391  // Bloom filter, we have to clear the whole filter which could
1392  // cause extra voting rounds.
1393  g_avalanche->clearFinalizedItems();
1394  }
1395 
1396  return true;
1397  },
1398  };
1399 }
1400 
1402  return RPCHelpMan{
1403  "isfinalblock",
1404  "Check if a block has been finalized by avalanche votes.\n",
1405  {
1407  "The hash of the block."},
1408  },
1409  RPCResult{RPCResult::Type::BOOL, "success",
1410  "Whether the block has been finalized by avalanche votes."},
1411  RPCExamples{HelpExampleRpc("isfinalblock", "<block hash>") +
1412  HelpExampleCli("isfinalblock", "<block hash>")},
1413  [&](const RPCHelpMan &self, const Config &config,
1414  const JSONRPCRequest &request) -> UniValue {
1415  if (!g_avalanche) {
1417  "Avalanche is not initialized");
1418  }
1419 
1420  if (!g_avalanche->isQuorumEstablished()) {
1422  "Avalanche is not ready to poll yet.");
1423  }
1424 
1425  ChainstateManager &chainman = EnsureAnyChainman(request.context);
1426  const BlockHash blockhash(
1427  ParseHashV(request.params[0], "blockhash"));
1428  const CBlockIndex *pindex;
1429 
1430  {
1431  LOCK(cs_main);
1432  pindex = chainman.m_blockman.LookupBlockIndex(blockhash);
1433 
1434  if (!pindex) {
1436  "Block not found");
1437  }
1438  }
1439 
1440  return chainman.ActiveChainstate().IsBlockAvalancheFinalized(
1441  pindex);
1442  },
1443  };
1444 }
1445 
1447  return RPCHelpMan{
1448  "isfinaltransaction",
1449  "Check if a transaction has been finalized by avalanche votes.\n",
1450  {
1452  "The id of the transaction."},
1454  "The block in which to look for the transaction"},
1455  },
1456  RPCResult{
1457  RPCResult::Type::BOOL, "success",
1458  "Whether the transaction has been finalized by avalanche votes."},
1459  RPCExamples{HelpExampleRpc("isfinaltransaction", "<txid> <blockhash>") +
1460  HelpExampleCli("isfinaltransaction", "<txid> <blockhash>")},
1461  [&](const RPCHelpMan &self, const Config &config,
1462  const JSONRPCRequest &request) -> UniValue {
1463  if (!g_avalanche) {
1465  "Avalanche is not initialized");
1466  }
1467 
1468  const NodeContext &node = EnsureAnyNodeContext(request.context);
1469  ChainstateManager &chainman = EnsureChainman(node);
1470  const CTxMemPool &mempool = EnsureMemPool(node);
1471  const TxId txid = TxId(ParseHashV(request.params[0], "txid"));
1472  CBlockIndex *pindex = nullptr;
1473 
1474  if (!request.params[1].isNull()) {
1475  const BlockHash blockhash(
1476  ParseHashV(request.params[1], "blockhash"));
1477 
1478  LOCK(cs_main);
1479  pindex = chainman.m_blockman.LookupBlockIndex(blockhash);
1480  if (!pindex) {
1482  "Block not found");
1483  }
1484  }
1485 
1486  bool f_txindex_ready = false;
1487  if (g_txindex && !pindex) {
1488  f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain();
1489  }
1490 
1491  BlockHash hash_block;
1492  const CTransactionRef tx = GetTransaction(
1493  pindex, &mempool, txid, config.GetChainParams().GetConsensus(),
1494  hash_block);
1495 
1496  if (!g_avalanche->isQuorumEstablished()) {
1498  "Avalanche is not ready to poll yet.");
1499  }
1500 
1501  if (!tx) {
1502  std::string errmsg;
1503  if (pindex) {
1504  if (WITH_LOCK(::cs_main,
1505  return !pindex->nStatus.hasData())) {
1507  "Block data not downloaded yet.");
1508  }
1509  errmsg = "No such transaction found in the provided block.";
1510  } else if (!g_txindex) {
1511  errmsg = "No such transaction. Use -txindex or provide a "
1512  "block hash to enable blockchain transaction "
1513  "queries.";
1514  } else if (!f_txindex_ready) {
1515  errmsg = "No such transaction. Blockchain transactions are "
1516  "still in the process of being indexed.";
1517  } else {
1518  errmsg = "No such mempool or blockchain transaction.";
1519  }
1521  }
1522 
1523  if (!pindex) {
1524  LOCK(cs_main);
1525  pindex = chainman.m_blockman.LookupBlockIndex(hash_block);
1526  }
1527 
1528  if (!tx) {
1529  // Tx not found, we should have raised an error at this stage
1530  return false;
1531  }
1532 
1533  if (mempool.isAvalancheFinalized(txid)) {
1534  // The transaction is finalized
1535  return true;
1536  }
1537 
1538  // Return true if the tx is in a finalized block
1539  return !node.mempool->exists(txid) &&
1540  chainman.ActiveChainstate().IsBlockAvalancheFinalized(
1541  pindex);
1542  },
1543  };
1544 }
1545 
1547  return RPCHelpMan{
1548  "reconsideravalancheproof",
1549  "Reconsider a known avalanche proof.\n",
1550  {
1552  "The hex encoded avalanche proof."},
1553  },
1554  RPCResult{
1556  "success",
1557  "Whether the proof has been successfully registered.",
1558  },
1559  RPCExamples{HelpExampleRpc("reconsideravalancheproof", "<proof hex>")},
1560  [&](const RPCHelpMan &self, const Config &config,
1561  const JSONRPCRequest &request) -> UniValue {
1562  if (!g_avalanche) {
1564  "Avalanche is not initialized");
1565  }
1566 
1567  auto proof = RCUPtr<avalanche::Proof>::make();
1568  NodeContext &node = EnsureAnyNodeContext(request.context);
1569 
1570  // Verify the proof. Note that this is redundant with the
1571  // verification done when adding the proof to the pool, but we get a
1572  // chance to give a better error message.
1573  verifyProofOrThrow(node, *proof, request.params[0].get_str());
1574 
1575  // There is no way to selectively clear the invalidation status of
1576  // a single proof, so we clear the whole Bloom filter. This could
1577  // cause extra voting rounds.
1578  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
1579  if (pm.isInvalid(proof->getId())) {
1580  pm.clearAllInvalid();
1581  }
1582  });
1583 
1584  // Add the proof to the pool if we don't have it already. Since the
1585  // proof verification has already been done, a failure likely
1586  // indicates that there already is a proof with conflicting utxos.
1588  if (!registerProofIfNeeded(proof, state)) {
1590  strprintf("%s (%s)\n",
1591  state.GetRejectReason(),
1592  state.GetDebugMessage()));
1593  }
1594 
1595  return g_avalanche->withPeerManager(
1596  [&](const avalanche::PeerManager &pm) {
1597  return pm.isBoundToPeer(proof->getId());
1598  });
1599  },
1600  };
1601 }
1602 
1604  return RPCHelpMan{
1605  "sendavalancheproof",
1606  "Broadcast an avalanche proof.\n",
1607  {
1609  "The avalanche proof to broadcast."},
1610  },
1611  RPCResult{RPCResult::Type::BOOL, "success",
1612  "Whether the proof was sent successfully or not."},
1613  RPCExamples{HelpExampleRpc("sendavalancheproof", "<proof>")},
1614  [&](const RPCHelpMan &self, const Config &config,
1615  const JSONRPCRequest &request) -> UniValue {
1616  if (!g_avalanche) {
1618  "Avalanche is not initialized");
1619  }
1620 
1621  auto proof = RCUPtr<avalanche::Proof>::make();
1622  NodeContext &node = EnsureAnyNodeContext(request.context);
1623 
1624  // Verify the proof. Note that this is redundant with the
1625  // verification done when adding the proof to the pool, but we get a
1626  // chance to give a better error message.
1627  verifyProofOrThrow(node, *proof, request.params[0].get_str());
1628 
1629  // Add the proof to the pool if we don't have it already. Since the
1630  // proof verification has already been done, a failure likely
1631  // indicates that there already is a proof with conflicting utxos.
1632  const avalanche::ProofId &proofid = proof->getId();
1634  if (!registerProofIfNeeded(proof, state)) {
1636  strprintf("%s (%s)\n",
1637  state.GetRejectReason(),
1638  state.GetDebugMessage()));
1639  }
1640 
1641  g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) {
1642  pm.addUnbroadcastProof(proofid);
1643  });
1644 
1645  if (node.peerman) {
1646  node.peerman->RelayProof(proofid);
1647  }
1648 
1649  return true;
1650  },
1651  };
1652 }
1653 
1655  return RPCHelpMan{
1656  "verifyavalancheproof",
1657  "Verify an avalanche proof is valid and return the error otherwise.\n",
1658  {
1660  "Proof to verify."},
1661  },
1662  RPCResult{RPCResult::Type::BOOL, "success",
1663  "Whether the proof is valid or not."},
1664  RPCExamples{HelpExampleRpc("verifyavalancheproof", "\"<proof>\"")},
1665  [&](const RPCHelpMan &self, const Config &config,
1666  const JSONRPCRequest &request) -> UniValue {
1667  RPCTypeCheck(request.params, {UniValue::VSTR});
1668 
1669  avalanche::Proof proof;
1670  verifyProofOrThrow(EnsureAnyNodeContext(request.context), proof,
1671  request.params[0].get_str());
1672 
1673  return true;
1674  },
1675  };
1676 }
1677 
1679  return RPCHelpMan{
1680  "verifyavalanchedelegation",
1681  "Verify an avalanche delegation is valid and return the error "
1682  "otherwise.\n",
1683  {
1685  "The avalanche proof delegation to verify."},
1686  },
1687  RPCResult{RPCResult::Type::BOOL, "success",
1688  "Whether the delegation is valid or not."},
1689  RPCExamples{HelpExampleRpc("verifyavalanchedelegation", "\"<proof>\"")},
1690  [&](const RPCHelpMan &self, const Config &config,
1691  const JSONRPCRequest &request) -> UniValue {
1692  RPCTypeCheck(request.params, {UniValue::VSTR});
1693 
1694  avalanche::Delegation delegation;
1695  CPubKey dummy;
1696  verifyDelegationOrThrow(delegation, request.params[0].get_str(),
1697  dummy);
1698 
1699  return true;
1700  },
1701  };
1702 }
1703 
1705  // clang-format off
1706  static const CRPCCommand commands[] = {
1707  // category actor (function)
1708  // ----------------- --------------------
1709  { "avalanche", getavalanchekey, },
1710  { "avalanche", addavalanchenode, },
1711  { "avalanche", buildavalancheproof, },
1712  { "avalanche", decodeavalancheproof, },
1713  { "avalanche", delegateavalancheproof, },
1714  { "avalanche", decodeavalanchedelegation, },
1715  { "avalanche", getavalancheinfo, },
1716  { "avalanche", getavalanchepeerinfo, },
1717  { "avalanche", getavalancheproofs, },
1718  { "avalanche", getstakingreward, },
1719  { "avalanche", setstakingreward, },
1720  { "avalanche", getremoteproofs, },
1721  { "avalanche", getrawavalancheproof, },
1722  { "avalanche", invalidateavalancheproof, },
1723  { "avalanche", isfinalblock, },
1724  { "avalanche", isfinaltransaction, },
1725  { "avalanche", reconsideravalancheproof, },
1726  { "avalanche", sendavalancheproof, },
1727  { "avalanche", verifyavalancheproof, },
1728  { "avalanche", verifyavalanchedelegation, },
1729  };
1730  // clang-format on
1731 
1732  for (const auto &c : commands) {
1733  t.appendCommand(c.name, &c);
1734  }
1735 }
std::unique_ptr< avalanche::Processor > g_avalanche
Global avalanche instance.
Definition: processor.cpp:38
#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:26
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: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:209
bool isAvalancheFinalized(const TxId &txid) const
Definition: txmempool.h:502
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1144
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1273
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:30
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:119
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:80
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
void ScriptPubKeyToUniv(const CScript &scriptPubKey, UniValue &out, bool fIncludeHex)
Definition: core_write.cpp:190
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
std::string EncodeDestination(const CTxDestination &dest, const Config &config)
Definition: key_io.cpp:167
CTxDestination DecodeDestination(const std::string &addr, const CChainParams &params)
Definition: key_io.cpp:174
CKey DecodeSecret(const std::string &str)
Definition: key_io.cpp:77
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:1348
static RPCHelpMan delegateavalancheproof()
Definition: avalanche.cpp:474
static RPCHelpMan getremoteproofs()
Definition: avalanche.cpp:1220
static RPCHelpMan decodeavalanchedelegation()
Definition: avalanche.cpp:554
static RPCHelpMan sendavalancheproof()
Definition: avalanche.cpp:1603
static RPCHelpMan getavalancheproofs()
Definition: avalanche.cpp:954
static void verifyDelegationOrThrow(avalanche::Delegation &dg, const std::string &dgHex, CPubKey &auth)
Definition: avalanche.cpp:81
static RPCHelpMan getrawavalancheproof()
Definition: avalanche.cpp:1276
static void verifyProofOrThrow(const NodeContext &node, avalanche::Proof &proof, const std::string &proofHex)
Definition: avalanche.cpp:95
void RegisterAvalancheRPCCommands(CRPCTable &t)
Definition: avalanche.cpp:1704
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:1678
static RPCHelpMan setstakingreward()
Definition: avalanche.cpp:1151
static RPCHelpMan isfinalblock()
Definition: avalanche.cpp:1401
static RPCHelpMan reconsideravalancheproof()
Definition: avalanche.cpp:1546
static RPCHelpMan isfinaltransaction()
Definition: avalanche.cpp:1446
static RPCHelpMan getstakingreward()
Definition: avalanche.cpp:1018
static RPCHelpMan getavalanchepeerinfo()
Definition: avalanche.cpp:852
static RPCHelpMan verifyavalancheproof()
Definition: avalanche.cpp:1654
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:152
bool IsDeprecatedRPCEnabled(const ArgsManager &args, const std::string &method)
Definition: server.cpp:410
ArgsManager & EnsureAnyArgsman(const std::any &context)
Definition: server_util.cpp:46
ChainstateManager & EnsureAnyChainman(const std::any &context)
Definition: server_util.cpp:57
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:50
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
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:80
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