Bitcoin ABC 0.34.2
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 <addresstype.h>
9#include <coins.h>
10#include <common/args.h>
11#include <hash.h>
12#include <policy/policy.h>
13#include <script/solver.h>
14#include <streams.h>
15#include <util/strencodings.h>
16#include <util/translation.h>
17#include <validation.h>
18
19#include <tinyformat.h>
20
21#include <limits>
22#include <numeric>
23#include <unordered_set>
24#include <variant>
25
26namespace avalanche {
27
28StakeCommitment::StakeCommitment(int64_t expirationTime,
29 const CPubKey &master) {
30 HashWriter ss{};
31 ss << expirationTime;
32 ss << master;
33 const uint256 &hash = ss.GetHash();
34 std::memcpy(m_data.data(), hash.data(), sizeof(m_data));
35}
36
38 HashWriter ss{};
39 ss << *this;
40 stakeid = StakeId(ss.GetHash());
41}
42
43uint256 Stake::getHash(const StakeCommitment &commitment) const {
44 HashWriter ss{};
45 ss << commitment;
46 ss << *this;
47 return ss.GetHash();
48}
49
50bool SignedStake::verify(const StakeCommitment &commitment) const {
51 return stake.getPubkey().VerifySchnorr(stake.getHash(commitment), sig);
52}
53
54bool Proof::FromHex(Proof &proof, const std::string &hexProof,
55 bilingual_str &errorOut) {
56 if (!IsHex(hexProof)) {
57 errorOut = _("Proof must be an hexadecimal string.");
58 return false;
59 }
60
61 DataStream ss{ParseHex(hexProof)};
62
63 try {
64 ss >> proof;
65 } catch (std::exception &e) {
66 errorOut = strprintf(_("Proof has invalid format: %s"), e.what());
67 return false;
68 }
69
70 return true;
71}
72
73std::string Proof::ToHex() const {
74 DataStream ss{};
75 ss << *this;
76 return HexStr(ss);
77}
78
80 HashWriter ss{};
81 ss << sequence;
82 ss << expirationTime;
84
85 WriteCompactSize(ss, stakes.size());
86 for (const SignedStake &s : stakes) {
87 ss << s.getStake();
88 }
89
90 limitedProofId = LimitedProofId(ss.GetHash());
92}
93
96}
97
98uint32_t Proof::amountToScore(Amount amount) {
99 amount = MoneyClamp(amount);
100 // After clamping, (100 * amount) is guaranteed not to overflow int64_t,
101 // and the resulting score fits in uint32_t.
102 static_assert(MAX_MONEY <=
103 (std::numeric_limits<int64_t>::max() / 100) * SATOSHI);
104 static_assert((100 * MAX_MONEY) / COIN <=
105 std::numeric_limits<uint32_t>::max());
106 return (100 * amount) / COIN;
107}
108
110 // Each add is at most MAX_MONEY + MAX_MONEY, which fits in int64_t.
111 static_assert(MAX_MONEY <=
112 (std::numeric_limits<int64_t>::max() / 2) * SATOSHI);
113 return std::accumulate(
114 stakes.begin(), stakes.end(), Amount::zero(),
115 [](Amount total, const SignedStake &ss) {
116 return MoneyClamp(total + MoneyClamp(ss.getStake().getAmount()));
117 });
118}
119
120static bool IsStandardPayoutScript(const CScript &scriptPubKey) {
121 // Check the script's standardness against the default max OP_RETURN size,
122 // so that a proof's validity is not affected by a local relay policy
123 // parameter (see -datacarriersize config option)
124 TxoutType scriptType;
125 return IsStandard(scriptPubKey, MAX_OP_RETURN_RELAY, scriptType);
126}
127
128bool Proof::verify(const Amount &stakeUtxoDustThreshold,
129 ProofValidationState &state) const {
130 if (stakes.empty()) {
131 return state.Invalid(ProofValidationResult::NO_STAKE, "no-stake");
132 }
133
134 if (stakes.size() > AVALANCHE_MAX_PROOF_STAKES) {
135 return state.Invalid(
137 strprintf("%u > %u", stakes.size(), AVALANCHE_MAX_PROOF_STAKES));
138 }
139
142 "payout-script-non-standard");
143 }
144
147 "invalid-proof-signature");
148 }
149
150 StakeId prevId = uint256::ZERO;
151 std::unordered_set<COutPoint, SaltedOutpointHasher> utxos;
152 for (const SignedStake &ss : stakes) {
153 const Stake &s = ss.getStake();
154 if (s.getAmount() < stakeUtxoDustThreshold) {
156 "amount-below-dust-threshold",
157 strprintf("%s < %s", s.getAmount().ToString(),
158 stakeUtxoDustThreshold.ToString()));
159 }
160
161 if (s.getId() < prevId) {
163 "wrong-stake-ordering");
164 }
165 prevId = s.getId();
166
167 if (!utxos.insert(s.getUTXO()).second) {
169 "duplicated-stake");
170 }
171
172 if (!ss.verify(getStakeCommitment())) {
173 return state.Invalid(
175 "invalid-stake-signature",
176 strprintf("TxId: %s", s.getUTXO().GetTxId().ToString()));
177 }
178 }
179
180 return true;
181}
182
183bool Proof::verify(const Amount &stakeUtxoDustThreshold,
184 const ChainstateManager &chainman,
185 ProofValidationState &state) const {
187 if (!verify(stakeUtxoDustThreshold, state)) {
188 // state is set by verify.
189 return false;
190 }
191
192 const CBlockIndex *activeTip = chainman.ActiveTip();
193 const int64_t tipMedianTimePast =
194 activeTip ? activeTip->GetMedianTimePast() : 0;
195 if (expirationTime > 0 && tipMedianTimePast >= expirationTime) {
196 return state.Invalid(ProofValidationResult::EXPIRED, "expired-proof");
197 }
198
199 const int64_t activeHeight = chainman.ActiveHeight();
200 const int64_t stakeUtxoMinConfirmations =
201 gArgs.GetIntArg("-avaproofstakeutxoconfirmations",
203
204 for (const SignedStake &ss : stakes) {
205 const Stake &s = ss.getStake();
206 const COutPoint &utxo = s.getUTXO();
207
208 auto coin{chainman.ActiveChainstate().CoinsTip().GetCoin(utxo)};
209 if (!coin) {
210 // The coins are not in the UTXO set.
212 "utxo-missing-or-spent");
213 }
214
215 if (s.isCoinbase() != coin->IsCoinBase()) {
216 return state.Invalid(
217 ProofValidationResult::COINBASE_MISMATCH, "coinbase-mismatch",
218 strprintf("expected %s, found %s",
219 s.isCoinbase() ? "true" : "false",
220 coin->IsCoinBase() ? "true" : "false"));
221 }
222
223 if (s.getHeight() != coin->GetHeight()) {
225 "height-mismatch",
226 strprintf("expected %u, found %u",
227 s.getHeight(), coin->GetHeight()));
228 }
229
230 const CTxOut &out = coin->GetTxOut();
231 if (s.getAmount() != out.nValue) {
232 // Wrong amount.
233 return state.Invalid(
235 strprintf("expected %s, found %s", s.getAmount().ToString(),
236 out.nValue.ToString()));
237 }
238
239 CTxDestination dest;
240 if (!ExtractDestination(out.scriptPubKey, dest)) {
241 // Can't extract destination.
242 return state.Invalid(
244 "non-standard-destination");
245 }
246
247 PKHash *pkhash = std::get_if<PKHash>(&dest);
248 if (!pkhash) {
249 // Only PKHash are supported.
250 return state.Invalid(
252 "destination-type-not-supported");
253 }
254
255 const CPubKey &pubkey = s.getPubkey();
256 if (*pkhash != PKHash(pubkey)) {
257 // Wrong pubkey.
259 "destination-mismatch");
260 }
261
262 if ((s.getHeight() + stakeUtxoMinConfirmations - 1) > activeHeight) {
263 return state.Invalid(
265 strprintf("TxId: %s, block height: %d, chaintip height: %d",
266 s.getUTXO().GetTxId().ToString(), s.getHeight(),
267 activeHeight));
268 }
269 }
270
271 return true;
272}
273
274} // namespace avalanche
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: addresstype.cpp:33
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: addresstype.h:54
static constexpr Amount SATOSHI
Definition: amount.h:153
static constexpr Amount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition: amount.h:176
Amount MoneyClamp(const Amount nValue)
Definition: amount.h:181
static constexpr Amount COIN
Definition: amount.h:154
ArgsManager gArgs
Definition: args.cpp:39
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:494
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
int64_t GetMedianTimePast() const
Definition: blockindex.h:171
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:206
An output of a transaction.
Definition: transaction.h:128
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1176
SnapshotCompletionResult MaybeCompleteSnapshotValidation() EXCLUSIVE_LOCKS_REQUIRED(const CBlockIndex *GetSnapshotBaseBlock() const EXCLUSIVE_LOCKS_REQUIRED(Chainstate ActiveChainstate)() const
Once the background validation chainstate has reached the height which is the base of the UTXO snapsh...
Definition: validation.h:1446
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1453
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1450
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:118
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:99
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
Definition: validation.h:101
static bool FromHex(Proof &proof, const std::string &hexProof, bilingual_str &errorOut)
Definition: proof.cpp:54
bool verify(const Amount &stakeUtxoDustThreshold, ProofValidationState &state) const
Definition: proof.cpp:128
int64_t expirationTime
Definition: proof.h:102
void computeProofId()
Definition: proof.cpp:79
Amount getStakedAmount() const
Definition: proof.cpp:109
CScript payoutScriptPubKey
Definition: proof.h:105
std::string ToHex() const
Definition: proof.cpp:73
void computeScore()
Definition: proof.cpp:94
LimitedProofId limitedProofId
Definition: proof.h:108
uint64_t sequence
Definition: proof.h:101
uint32_t score
Definition: proof.h:112
std::vector< SignedStake > stakes
Definition: proof.h:104
CPubKey master
Definition: proof.h:103
StakeCommitment getStakeCommitment() const
Definition: proof.h:169
ProofId proofid
Definition: proof.h:109
SchnorrSig signature
Definition: proof.h:106
static uint32_t amountToScore(Amount amount)
Definition: proof.cpp:98
SchnorrSig sig
Definition: proof.h:85
bool verify(const StakeCommitment &commitment) const
Definition: proof.cpp:50
uint256 getHash(const StakeCommitment &commitment) const
Definition: proof.cpp:43
Amount getAmount() const
Definition: proof.h:73
bool isCoinbase() const
Definition: proof.h:75
uint32_t getHeight() const
Definition: proof.h:74
StakeId stakeid
Definition: proof.h:55
void computeStakeId()
Definition: proof.cpp:37
const CPubKey & getPubkey() const
Definition: proof.h:76
const COutPoint & getUTXO() const
Definition: proof.h:72
const StakeId & getId() const
Definition: proof.h:80
constexpr const uint8_t * data() const
Definition: uint256.h:119
std::array< uint8_t, WIDTH > m_data
Definition: uint256.h:29
256-bit opaque blob.
Definition: uint256.h:199
static const uint256 ZERO
Definition: uint256.h:210
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 HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
uint256 StakeId
Definition: proof.h:42
static bool IsStandardPayoutScript(const CScript &scriptPubKey)
Definition: proof.cpp:120
bool IsStandard(const CScript &scriptPubKey, const std::optional< unsigned > &max_datacarrier_bytes, TxoutType &whichType)
Definition: policy.cpp:40
static constexpr unsigned int MAX_OP_RETURN_RELAY
Default setting for nMaxDatacarrierBytes.
Definition: policy.h:77
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:33
static constexpr int AVALANCHE_MAX_PROOF_STAKES
How many UTXOs can be used for a single proof.
Definition: proof.h:27
void WriteCompactSize(SizeComputer &os, uint64_t nSize)
Definition: serialize.h:1260
TxoutType
Definition: solver.h:15
Definition: amount.h:23
static constexpr Amount zero() noexcept
Definition: amount.h:36
std::string ToString() const
Definition: amount.cpp:29
ProofId computeProofId(const CPubKey &proofMaster) const
Definition: proofid.cpp:12
StakeCommitment(int64_t expirationTime, const CPubKey &master)
Definition: proof.cpp:28
Bilingual messages:
Definition: translation.h:17
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1203
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:68
template std::vector< std::byte > ParseHex(std::string_view)
bool IsHex(std::string_view str)
Returns true if each character in str is a hex character, and has an even number of hex digits.
AssertLockHeld(pool.cs)