Bitcoin ABC  0.26.3
P2P Digital Currency
proof.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/proof.h>
6 
7 #include <avalanche/validation.h>
8 #include <coins.h>
9 #include <hash.h>
10 #include <policy/policy.h>
11 #include <script/standard.h>
12 #include <streams.h>
13 #include <util/strencodings.h>
14 #include <util/system.h>
15 #include <util/translation.h>
16 
17 #include <tinyformat.h>
18 
19 #include <numeric>
20 #include <unordered_set>
21 
22 namespace avalanche {
23 
24 StakeCommitment::StakeCommitment(int64_t expirationTime,
25  const CPubKey &master) {
26  CHashWriter ss(SER_GETHASH, 0);
27  ss << expirationTime;
28  ss << master;
29  const uint256 &hash = ss.GetHash();
30  memcpy(m_data, hash.data(), sizeof(m_data));
31 }
32 
34  CHashWriter ss(SER_GETHASH, 0);
35  ss << *this;
36  stakeid = StakeId(ss.GetHash());
37 }
38 
39 uint256 Stake::getHash(const StakeCommitment &commitment) const {
40  CHashWriter ss(SER_GETHASH, 0);
41  ss << commitment;
42  ss << *this;
43  return ss.GetHash();
44 }
45 
46 bool SignedStake::verify(const StakeCommitment &commitment) const {
47  return stake.getPubkey().VerifySchnorr(stake.getHash(commitment), sig);
48 }
49 
50 bool Proof::FromHex(Proof &proof, const std::string &hexProof,
51  bilingual_str &errorOut) {
52  if (!IsHex(hexProof)) {
53  errorOut = _("Proof must be an hexadecimal string.");
54  return false;
55  }
56 
58 
59  try {
60  ss >> proof;
61  } catch (std::exception &e) {
62  errorOut = strprintf(_("Proof has invalid format: %s"), e.what());
63  return false;
64  }
65 
66  return true;
67 }
68 
69 std::string Proof::ToHex() const {
71  ss << *this;
72  return HexStr(ss);
73 }
74 
76  CHashWriter ss(SER_GETHASH, 0);
77  ss << sequence;
78  ss << expirationTime;
79  ss << payoutScriptPubKey;
80 
81  WriteCompactSize(ss, stakes.size());
82  for (const SignedStake &s : stakes) {
83  ss << s.getStake();
84  }
85 
88 }
89 
91  Amount total = Amount::zero();
92  for (const SignedStake &s : stakes) {
93  total += s.getStake().getAmount();
94  }
95 
96  score = amountToScore(total);
97 }
98 
99 uint32_t Proof::amountToScore(Amount amount) {
100  return (100 * amount) / COIN;
101 }
102 
104  return std::accumulate(stakes.begin(), stakes.end(), Amount::zero(),
105  [](const Amount current, const SignedStake &ss) {
106  return current + ss.getStake().getAmount();
107  });
108 }
109 
110 bool Proof::verify(const Amount &stakeUtxoDustThreshold,
111  ProofValidationState &state) const {
112  if (stakes.empty()) {
113  return state.Invalid(ProofValidationResult::NO_STAKE, "no-stake");
114  }
115 
116  if (stakes.size() > AVALANCHE_MAX_PROOF_STAKES) {
117  return state.Invalid(
118  ProofValidationResult::TOO_MANY_UTXOS, "too-many-utxos",
119  strprintf("%u > %u", stakes.size(), AVALANCHE_MAX_PROOF_STAKES));
120  }
121 
122  TxoutType scriptType;
123  if (!IsStandard(payoutScriptPubKey, scriptType)) {
125  "payout-script-non-standard");
126  }
127 
130  "invalid-proof-signature");
131  }
132 
133  StakeId prevId = uint256::ZERO;
134  std::unordered_set<COutPoint, SaltedOutpointHasher> utxos;
135  for (const SignedStake &ss : stakes) {
136  const Stake &s = ss.getStake();
137  if (s.getAmount() < stakeUtxoDustThreshold) {
139  "amount-below-dust-threshold",
140  strprintf("%s < %s", s.getAmount().ToString(),
141  stakeUtxoDustThreshold.ToString()));
142  }
143 
144  if (s.getId() < prevId) {
146  "wrong-stake-ordering");
147  }
148  prevId = s.getId();
149 
150  if (!utxos.insert(s.getUTXO()).second) {
152  "duplicated-stake");
153  }
154 
155  if (!ss.verify(getStakeCommitment())) {
156  return state.Invalid(
158  "invalid-stake-signature",
159  strprintf("TxId: %s", s.getUTXO().GetTxId().ToString()));
160  }
161  }
162 
163  return true;
164 }
165 
166 bool Proof::verify(const Amount &stakeUtxoDustThreshold,
167  const ChainstateManager &chainman,
168  ProofValidationState &state) const {
170  if (!verify(stakeUtxoDustThreshold, state)) {
171  // state is set by verify.
172  return false;
173  }
174 
175  const CBlockIndex *activeTip = chainman.ActiveTip();
176  const int64_t tipMedianTimePast =
177  activeTip ? activeTip->GetMedianTimePast() : 0;
178  if (expirationTime > 0 && tipMedianTimePast >= expirationTime) {
179  return state.Invalid(ProofValidationResult::EXPIRED, "expired-proof");
180  }
181 
182  const int64_t activeHeight = chainman.ActiveHeight();
183  const int64_t stakeUtxoMinConfirmations =
184  gArgs.GetIntArg("-avaproofstakeutxoconfirmations",
186 
187  for (const SignedStake &ss : stakes) {
188  const Stake &s = ss.getStake();
189  const COutPoint &utxo = s.getUTXO();
190 
191  Coin coin;
192  if (!chainman.ActiveChainstate().CoinsTip().GetCoin(utxo, coin)) {
193  // The coins are not in the UTXO set.
195  "utxo-missing-or-spent");
196  }
197 
198  if ((s.getHeight() + stakeUtxoMinConfirmations - 1) > activeHeight) {
199  return state.Invalid(
200  ProofValidationResult::IMMATURE_UTXO, "immature-utxo",
201  strprintf("TxId: %s, block height: %d, chaintip height: %d",
202  s.getUTXO().GetTxId().ToString(), s.getHeight(),
203  activeHeight));
204  }
205 
206  if (s.isCoinbase() != coin.IsCoinBase()) {
207  return state.Invalid(
208  ProofValidationResult::COINBASE_MISMATCH, "coinbase-mismatch",
209  strprintf("expected %s, found %s",
210  s.isCoinbase() ? "true" : "false",
211  coin.IsCoinBase() ? "true" : "false"));
212  }
213 
214  if (s.getHeight() != coin.GetHeight()) {
216  "height-mismatch",
217  strprintf("expected %u, found %u",
218  s.getHeight(), coin.GetHeight()));
219  }
220 
221  const CTxOut &out = coin.GetTxOut();
222  if (s.getAmount() != out.nValue) {
223  // Wrong amount.
224  return state.Invalid(
225  ProofValidationResult::AMOUNT_MISMATCH, "amount-mismatch",
226  strprintf("expected %s, found %s", s.getAmount().ToString(),
227  out.nValue.ToString()));
228  }
229 
230  CTxDestination dest;
231  if (!ExtractDestination(out.scriptPubKey, dest)) {
232  // Can't extract destination.
233  return state.Invalid(
235  "non-standard-destination");
236  }
237 
238  PKHash *pkhash = boost::get<PKHash>(&dest);
239  if (!pkhash) {
240  // Only PKHash are supported.
241  return state.Invalid(
243  "destination-type-not-supported");
244  }
245 
246  const CPubKey &pubkey = s.getPubkey();
247  if (*pkhash != PKHash(pubkey)) {
248  // Wrong pubkey.
250  "destination-mismatch");
251  }
252  }
253 
254  return true;
255 }
256 
257 } // namespace avalanche
static constexpr Amount COIN
Definition: amount.h:154
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:112
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: system.cpp:593
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:23
int64_t GetMedianTimePast() const
Definition: blockindex.h:181
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: validation.h:781
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override
Retrieve the Coin (unspent transaction output) for a given outpoint.
Definition: coins.cpp:91
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:197
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:100
uint256 GetHash()
Compute the double-SHA256 hash of all data written to this object.
Definition: hash.h:123
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:22
const TxId & GetTxId() const
Definition: transaction.h:37
An encapsulated public key.
Definition: pubkey.h:31
bool VerifySchnorr(const uint256 &hash, const std::array< uint8_t, SCHNORR_SIZE > &sig) const
Verify a Schnorr signature (=64 bytes).
Definition: pubkey.cpp:200
An output of a transaction.
Definition: transaction.h:130
CScript scriptPubKey
Definition: transaction.h:133
Amount nValue
Definition: transaction.h:132
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1077
int ActiveHeight() const
Definition: validation.h:1213
CChainState & ActiveChainstate() const
The most-work chain.
CBlockIndex * ActiveTip() const
Definition: validation.h:1214
A UTXO entry.
Definition: coins.h:27
uint32_t GetHeight() const
Definition: coins.h:44
bool IsCoinBase() const
Definition: coins.h:45
CTxOut & GetTxOut()
Definition: coins.h:48
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
Definition: validation.h:102
static bool FromHex(Proof &proof, const std::string &hexProof, bilingual_str &errorOut)
Definition: proof.cpp:50
bool verify(const Amount &stakeUtxoDustThreshold, ProofValidationState &state) const
Definition: proof.cpp:110
int64_t expirationTime
Definition: proof.h:104
void computeProofId()
Definition: proof.cpp:75
Amount getStakedAmount() const
Definition: proof.cpp:103
CScript payoutScriptPubKey
Definition: proof.h:107
std::string ToHex() const
Definition: proof.cpp:69
const StakeCommitment getStakeCommitment() const
Definition: proof.h:171
void computeScore()
Definition: proof.cpp:90
LimitedProofId limitedProofId
Definition: proof.h:110
uint64_t sequence
Definition: proof.h:103
uint32_t score
Definition: proof.h:114
std::vector< SignedStake > stakes
Definition: proof.h:106
CPubKey master
Definition: proof.h:105
ProofId proofid
Definition: proof.h:111
SchnorrSig signature
Definition: proof.h:108
static uint32_t amountToScore(Amount amount)
Definition: proof.cpp:99
SchnorrSig sig
Definition: proof.h:87
bool verify(const StakeCommitment &commitment) const
Definition: proof.cpp:46
uint256 getHash(const StakeCommitment &commitment) const
Definition: proof.cpp:39
const COutPoint & getUTXO() const
Definition: proof.h:74
const CPubKey & getPubkey() const
Definition: proof.h:78
const StakeId & getId() const
Definition: proof.h:82
Amount getAmount() const
Definition: proof.h:75
bool isCoinbase() const
Definition: proof.h:77
uint32_t getHeight() const
Definition: proof.h:76
StakeId stakeid
Definition: proof.h:57
void computeStakeId()
Definition: proof.cpp:33
const uint8_t * data() const
Definition: uint256.h:80
std::string ToString() const
Definition: uint256.h:78
uint8_t m_data[WIDTH]
Definition: uint256.h:19
256-bit opaque blob.
Definition: uint256.h:127
static const uint256 ZERO
Definition: uint256.h:132
uint256 StakeId
Definition: proof.h:44
bool IsStandard(const CScript &scriptPubKey, TxoutType &whichType)
Definition: policy.cpp:38
static constexpr int AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS
Minimum number of confirmations before a stake utxo is mature enough to be included into a proof.
Definition: proof.h:35
static constexpr int AVALANCHE_MAX_PROOF_STAKES
How many UTXOs can be used for a single proof.
Definition: proof.h:29
@ SER_NETWORK
Definition: serialize.h:166
@ SER_GETHASH
Definition: serialize.h:168
void WriteCompactSize(CSizeComputer &os, uint64_t nSize)
Definition: serialize.h:1255
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:161
boost::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:93
TxoutType
Definition: standard.h:46
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
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()
Definition: amount.h:42
std::string ToString() const
Definition: amount.cpp:22
ProofId computeProofId(const CPubKey &proofMaster) const
Definition: proofid.cpp:12
StakeCommitment(int64_t expirationTime, const CPubKey &master)
Definition: proof.cpp:24
Bilingual messages:
Definition: translation.h:17
ArgsManager gArgs
Definition: system.cpp:77
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1201
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:55
AssertLockHeld(pool.cs)
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11