Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core 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 <evo/providertx.h>
6 :
7 : #include <evo/dmn_types.h>
8 : #include <util/std23.h>
9 :
10 : #include <chainparams.h>
11 : #include <consensus/validation.h>
12 : #include <deploymentstatus.h>
13 : #include <hash.h>
14 : #include <script/standard.h>
15 : #include <tinyformat.h>
16 : #include <validation.h>
17 :
18 : namespace ProTxVersion {
19 : template <typename T>
20 146 : [[nodiscard]] uint16_t GetMaxFromDeployment(gsl::not_null<const CBlockIndex*> pindexPrev,
21 : const ChainstateManager& chainman, std::optional<bool> is_basic_override)
22 : {
23 146 : constexpr bool is_extaddr_eligible{std::is_same_v<std::decay_t<T>, CProRegTx> || std::is_same_v<std::decay_t<T>, CProUpServTx>};
24 146 : return ProTxVersion::GetMax(
25 146 : is_basic_override ? *is_basic_override
26 146 : : DeploymentActiveAfter(pindexPrev, chainman.GetConsensus(), Consensus::DEPLOYMENT_V19),
27 103 : is_extaddr_eligible ? DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24) : false);
28 : }
29 : template uint16_t GetMaxFromDeployment<CProRegTx>(gsl::not_null<const CBlockIndex*> pindexPrev,
30 : const ChainstateManager& chainman,
31 : std::optional<bool> is_basic_override);
32 : template uint16_t GetMaxFromDeployment<CProUpServTx>(gsl::not_null<const CBlockIndex*> pindexPrev,
33 : const ChainstateManager& chainman,
34 : std::optional<bool> is_basic_override);
35 : template uint16_t GetMaxFromDeployment<CProUpRegTx>(gsl::not_null<const CBlockIndex*> pindexPrev,
36 : const ChainstateManager& chainman,
37 : std::optional<bool> is_basic_override);
38 : template uint16_t GetMaxFromDeployment<CProUpRevTx>(gsl::not_null<const CBlockIndex*> pindexPrev,
39 : const ChainstateManager& chainman,
40 : std::optional<bool> is_basic_override);
41 : } // namespace ProTxVersion
42 :
43 : template <typename ProTx>
44 93 : bool IsNetInfoTriviallyValid(const ProTx& proTx, TxValidationState& state)
45 : {
46 93 : if (!proTx.netInfo->HasEntries(NetInfoPurpose::CORE_P2P)) {
47 : // Mandatory for all nodes
48 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
49 : }
50 93 : if (proTx.nType == MnType::Regular) {
51 : // Regular nodes shouldn't populate Platform-specific fields
52 91 : if (proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) ||
53 91 : proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
54 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
55 : }
56 91 : }
57 93 : if (proTx.netInfo->CanStorePlatform() && proTx.nType == MnType::Evo) {
58 : // Platform fields are mandatory for EvoNodes
59 0 : if (!proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) ||
60 0 : !proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
61 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
62 : }
63 0 : }
64 93 : return true;
65 93 : }
66 :
67 85 : bool CProRegTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
68 : TxValidationState& state) const
69 : {
70 85 : if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
71 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
72 : }
73 85 : if (nVersion < ProTxVersion::BasicBLS && nType == MnType::Evo) {
74 4 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-evo-version");
75 : }
76 81 : if (!IsValidMnType(nType)) {
77 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
78 : }
79 81 : if (nMode != 0) {
80 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-mode");
81 : }
82 :
83 81 : if (keyIDOwner.IsNull() || !pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
84 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null");
85 : }
86 79 : if (pubKeyOperator.IsLegacy() != (nVersion == ProTxVersion::LegacyBLS)) {
87 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-pubkey");
88 : }
89 79 : if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
90 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee");
91 : }
92 77 : if (netInfo->CanStorePlatform() != (nVersion == ProTxVersion::ExtAddr)) {
93 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-netinfo-version");
94 : }
95 77 : if (!netInfo->IsEmpty() && !IsNetInfoTriviallyValid(*this, state)) {
96 : // pass the state returned by the function above
97 0 : return false;
98 : }
99 154 : for (const auto& entry : netInfo->GetEntries()) {
100 77 : if (!entry.IsTriviallyValid()) {
101 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
102 : }
103 : }
104 :
105 77 : CTxDestination payoutDest;
106 77 : if (!ExtractDestination(scriptPayout, payoutDest)) {
107 : // should not happen as we checked script types before
108 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-dest");
109 : }
110 : // don't allow reuse of payout key for other keys (don't allow people to put the payee key onto an online server)
111 77 : if (payoutDest == CTxDestination(PKHash(keyIDOwner)) || payoutDest == CTxDestination(PKHash(keyIDVoting))) {
112 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-reuse");
113 : }
114 :
115 75 : if (nOperatorReward > 10000) {
116 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-reward");
117 : }
118 :
119 73 : return true;
120 85 : }
121 :
122 7 : std::string CProRegTx::MakeSignString() const
123 : {
124 7 : std::string s;
125 :
126 : // We only include the important stuff in the string form...
127 :
128 7 : CTxDestination destPayout;
129 7 : std::string strPayout;
130 7 : if (ExtractDestination(scriptPayout, destPayout)) {
131 7 : strPayout = EncodeDestination(destPayout);
132 7 : } else {
133 0 : strPayout = HexStr(scriptPayout);
134 : }
135 :
136 7 : s += strPayout + "|";
137 7 : s += strprintf("%d", nOperatorReward) + "|";
138 7 : s += EncodeDestination(PKHash(keyIDOwner)) + "|";
139 7 : s += EncodeDestination(PKHash(keyIDVoting)) + "|";
140 :
141 : // ... and also the full hash of the payload as a protection against malleability and replays
142 7 : s += ::SerializeHash(*this).ToString();
143 :
144 7 : return s;
145 7 : }
146 :
147 69 : std::string CProRegTx::ToString() const
148 : {
149 69 : CTxDestination dest;
150 69 : std::string payee = "unknown";
151 69 : if (ExtractDestination(scriptPayout, dest)) {
152 69 : payee = EncodeDestination(dest);
153 69 : }
154 :
155 69 : return strprintf("CProRegTx(nVersion=%d, nType=%d, collateralOutpoint=%s, netInfo=%s, nOperatorReward=%f, "
156 : "ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s, platformNodeID=%s%s)\n",
157 69 : nVersion, std23::to_underlying(nType), collateralOutpoint.ToStringShort(), netInfo->ToString(),
158 69 : (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(),
159 69 : EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(),
160 138 : (nVersion >= ProTxVersion::ExtAddr
161 0 : ? ""
162 69 : : strprintf(", platformP2PPort=%d, platformHTTPPort=%d", platformP2PPort, platformHTTPPort)));
163 69 : }
164 :
165 18 : bool CProUpServTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
166 : TxValidationState& state) const
167 : {
168 18 : if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
169 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
170 : }
171 16 : if (nVersion < ProTxVersion::BasicBLS && nType == MnType::Evo) {
172 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-evo-version");
173 : }
174 16 : if (netInfo->CanStorePlatform() != (nVersion == ProTxVersion::ExtAddr)) {
175 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-netinfo-version");
176 : }
177 16 : if (netInfo->IsEmpty()) {
178 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
179 : }
180 16 : if (!IsNetInfoTriviallyValid(*this, state)) {
181 : // pass the state returned by the function above
182 0 : return false;
183 : }
184 32 : for (const auto& entry : netInfo->GetEntries()) {
185 16 : if (!entry.IsTriviallyValid()) {
186 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
187 : }
188 : }
189 :
190 16 : return true;
191 18 : }
192 :
193 8 : std::string CProUpServTx::ToString() const
194 : {
195 8 : CTxDestination dest;
196 8 : std::string payee = "unknown";
197 8 : if (ExtractDestination(scriptOperatorPayout, dest)) {
198 0 : payee = EncodeDestination(dest);
199 0 : }
200 :
201 8 : return strprintf("CProUpServTx(nVersion=%d, nType=%d, proTxHash=%s, netInfo=%s, operatorPayoutAddress=%s, "
202 : "platformNodeID=%s%s)\n",
203 8 : nVersion, std23::to_underlying(nType), proTxHash.ToString(), netInfo->ToString(), payee,
204 8 : platformNodeID.ToString(),
205 16 : (nVersion >= ProTxVersion::ExtAddr
206 0 : ? ""
207 8 : : strprintf(", platformP2PPort=%d, platformHTTPPort=%d", platformP2PPort, platformHTTPPort)));
208 8 : }
209 :
210 23 : bool CProUpRegTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
211 : TxValidationState& state) const
212 : {
213 23 : if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
214 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
215 : }
216 21 : if (nMode != 0) {
217 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-mode");
218 : }
219 :
220 21 : if (!pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
221 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null");
222 : }
223 19 : if (pubKeyOperator.IsLegacy() != (nVersion == ProTxVersion::LegacyBLS)) {
224 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-pubkey");
225 : }
226 19 : if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
227 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee");
228 : }
229 19 : return true;
230 23 : }
231 :
232 6 : std::string CProUpRegTx::ToString() const
233 : {
234 6 : CTxDestination dest;
235 6 : std::string payee = "unknown";
236 6 : if (ExtractDestination(scriptPayout, dest)) {
237 6 : payee = EncodeDestination(dest);
238 6 : }
239 :
240 6 : return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s)",
241 6 : nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee);
242 6 : }
243 :
244 20 : bool CProUpRevTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
245 : TxValidationState& state) const
246 : {
247 20 : if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
248 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
249 : }
250 :
251 : // nReason < CProUpRevTx::REASON_NOT_SPECIFIED is always `false` since
252 : // nReason is unsigned and CProUpRevTx::REASON_NOT_SPECIFIED == 0
253 18 : if (nReason > CProUpRevTx::REASON_LAST) {
254 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-reason");
255 : }
256 16 : return true;
257 20 : }
258 :
259 7 : std::string CProUpRevTx::ToString() const
260 : {
261 7 : return strprintf("CProUpRevTx(nVersion=%d, proTxHash=%s, nReason=%d)",
262 7 : nVersion, proTxHash.ToString(), nReason);
263 0 : }
|