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 8922 : [[nodiscard]] uint16_t GetMaxFromDeployment(gsl::not_null<const CBlockIndex*> pindexPrev,
21 : const ChainstateManager& chainman, std::optional<bool> is_basic_override)
22 : {
23 8922 : constexpr bool is_extaddr_eligible{std::is_same_v<std::decay_t<T>, CProRegTx> || std::is_same_v<std::decay_t<T>, CProUpServTx>};
24 8922 : return ProTxVersion::GetMax(
25 8922 : is_basic_override ? *is_basic_override
26 8349 : : DeploymentActiveAfter(pindexPrev, chainman.GetConsensus(), Consensus::DEPLOYMENT_V19),
27 8636 : 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 7607 : bool IsNetInfoTriviallyValid(const ProTx& proTx, TxValidationState& state)
45 : {
46 7607 : 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 7607 : if (proTx.nType == MnType::Regular) {
51 : // Regular nodes shouldn't populate Platform-specific fields
52 7015 : if (proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) ||
53 7015 : proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
54 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
55 : }
56 7015 : }
57 7607 : if (proTx.netInfo->CanStorePlatform() && proTx.nType == MnType::Evo) {
58 : // Platform fields are mandatory for EvoNodes
59 112 : if (!proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) ||
60 112 : !proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
61 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
62 : }
63 112 : }
64 7607 : return true;
65 7607 : }
66 :
67 3881 : bool CProRegTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
68 : TxValidationState& state) const
69 : {
70 3881 : if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
71 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
72 : }
73 3881 : if (nVersion < ProTxVersion::BasicBLS && nType == MnType::Evo) {
74 4 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-evo-version");
75 : }
76 3877 : if (!IsValidMnType(nType)) {
77 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
78 : }
79 3877 : if (nMode != 0) {
80 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-mode");
81 : }
82 :
83 3877 : if (keyIDOwner.IsNull() || !pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
84 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null");
85 : }
86 3875 : if (pubKeyOperator.IsLegacy() != (nVersion == ProTxVersion::LegacyBLS)) {
87 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-pubkey");
88 : }
89 3875 : if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
90 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee");
91 : }
92 3873 : if (netInfo->CanStorePlatform() != (nVersion == ProTxVersion::ExtAddr)) {
93 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-netinfo-version");
94 : }
95 3873 : if (!netInfo->IsEmpty() && !IsNetInfoTriviallyValid(*this, state)) {
96 : // pass the state returned by the function above
97 0 : return false;
98 : }
99 7868 : for (const auto& entry : netInfo->GetEntries()) {
100 3995 : if (!entry.IsTriviallyValid()) {
101 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
102 : }
103 : }
104 :
105 3873 : CTxDestination payoutDest;
106 3873 : 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 3873 : 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 3871 : if (nOperatorReward > 10000) {
116 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-reward");
117 : }
118 :
119 3869 : return true;
120 3881 : }
121 :
122 2659 : std::string CProRegTx::MakeSignString() const
123 : {
124 2659 : std::string s;
125 :
126 : // We only include the important stuff in the string form...
127 :
128 2659 : CTxDestination destPayout;
129 2659 : std::string strPayout;
130 2659 : if (ExtractDestination(scriptPayout, destPayout)) {
131 2659 : strPayout = EncodeDestination(destPayout);
132 2659 : } else {
133 0 : strPayout = HexStr(scriptPayout);
134 : }
135 :
136 2659 : s += strPayout + "|";
137 2659 : s += strprintf("%d", nOperatorReward) + "|";
138 2659 : s += EncodeDestination(PKHash(keyIDOwner)) + "|";
139 2659 : s += EncodeDestination(PKHash(keyIDVoting)) + "|";
140 :
141 : // ... and also the full hash of the payload as a protection against malleability and replays
142 2659 : s += ::SerializeHash(*this).ToString();
143 :
144 2659 : return s;
145 2659 : }
146 :
147 2789 : std::string CProRegTx::ToString() const
148 : {
149 2789 : CTxDestination dest;
150 2789 : std::string payee = "unknown";
151 2789 : if (ExtractDestination(scriptPayout, dest)) {
152 2789 : payee = EncodeDestination(dest);
153 2789 : }
154 :
155 2789 : 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 2789 : nVersion, std23::to_underlying(nType), collateralOutpoint.ToStringShort(), netInfo->ToString(),
158 2789 : (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(),
159 2789 : EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(),
160 5578 : (nVersion >= ProTxVersion::ExtAddr
161 40 : ? ""
162 2749 : : strprintf(", platformP2PPort=%d, platformHTTPPort=%d", platformP2PPort, platformHTTPPort)));
163 2789 : }
164 :
165 3800 : bool CProUpServTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
166 : TxValidationState& state) const
167 : {
168 3800 : if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
169 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
170 : }
171 3798 : if (nVersion < ProTxVersion::BasicBLS && nType == MnType::Evo) {
172 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-evo-version");
173 : }
174 3798 : if (netInfo->CanStorePlatform() != (nVersion == ProTxVersion::ExtAddr)) {
175 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-netinfo-version");
176 : }
177 3798 : if (netInfo->IsEmpty()) {
178 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
179 : }
180 3798 : if (!IsNetInfoTriviallyValid(*this, state)) {
181 : // pass the state returned by the function above
182 0 : return false;
183 : }
184 7872 : for (const auto& entry : netInfo->GetEntries()) {
185 4074 : if (!entry.IsTriviallyValid()) {
186 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
187 : }
188 : }
189 :
190 3798 : return true;
191 3800 : }
192 :
193 2897 : std::string CProUpServTx::ToString() const
194 : {
195 2897 : CTxDestination dest;
196 2897 : std::string payee = "unknown";
197 2897 : if (ExtractDestination(scriptOperatorPayout, dest)) {
198 1356 : payee = EncodeDestination(dest);
199 1356 : }
200 :
201 2897 : return strprintf("CProUpServTx(nVersion=%d, nType=%d, proTxHash=%s, netInfo=%s, operatorPayoutAddress=%s, "
202 : "platformNodeID=%s%s)\n",
203 2897 : nVersion, std23::to_underlying(nType), proTxHash.ToString(), netInfo->ToString(), payee,
204 2897 : platformNodeID.ToString(),
205 5794 : (nVersion >= ProTxVersion::ExtAddr
206 30 : ? ""
207 2867 : : strprintf(", platformP2PPort=%d, platformHTTPPort=%d", platformP2PPort, platformHTTPPort)));
208 2897 : }
209 :
210 159 : bool CProUpRegTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
211 : TxValidationState& state) const
212 : {
213 159 : if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
214 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
215 : }
216 157 : if (nMode != 0) {
217 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-mode");
218 : }
219 :
220 157 : if (!pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
221 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null");
222 : }
223 155 : if (pubKeyOperator.IsLegacy() != (nVersion == ProTxVersion::LegacyBLS)) {
224 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-pubkey");
225 : }
226 155 : if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
227 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee");
228 : }
229 155 : return true;
230 159 : }
231 :
232 126 : std::string CProUpRegTx::ToString() const
233 : {
234 126 : CTxDestination dest;
235 126 : std::string payee = "unknown";
236 126 : if (ExtractDestination(scriptPayout, dest)) {
237 126 : payee = EncodeDestination(dest);
238 126 : }
239 :
240 126 : return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s)",
241 126 : nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee);
242 126 : }
243 :
244 113 : bool CProUpRevTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
245 : TxValidationState& state) const
246 : {
247 113 : 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 111 : if (nReason > CProUpRevTx::REASON_LAST) {
254 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-reason");
255 : }
256 109 : return true;
257 113 : }
258 :
259 73 : std::string CProUpRevTx::ToString() const
260 : {
261 73 : return strprintf("CProUpRevTx(nVersion=%d, proTxHash=%s, nReason=%d)",
262 73 : nVersion, proTxHash.ToString(), nReason);
263 0 : }
|