Line data Source code
1 : /**
2 : * Copyright (c) Libmonero
3 : *
4 : * Permission is hereby granted, free of charge, to any person obtaining a copy
5 : * of this software and associated documentation files (the "Software"), to deal
6 : * in the Software without restriction, including without limitation the rights
7 : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : * copies of the Software, and to permit persons to whom the Software is
9 : * furnished to do so, subject to the following conditions:
10 : *
11 : * The above copyright notice and this permission notice shall be included in all
12 : * copies or substantial portions of the Software.
13 : *
14 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 : * SOFTWARE.
21 : */
22 :
23 : #ifndef MONERO_C_COMMON_H
24 : #define MONERO_C_COMMON_H
25 :
26 : #include "utils/monero_c_utils.h"
27 : #include "common/monero_c_rpc_connection.h"
28 :
29 : #include "rapidjson/stringbuffer.h"
30 : #include "rapidjson/writer.h"
31 :
32 : #include <boost/optional.hpp>
33 : #include <algorithm>
34 : #include <cstdio>
35 : #include <cstdlib>
36 : #include <cstring>
37 : #include <exception>
38 : #include <memory>
39 : #include <new>
40 : #include <stdexcept>
41 : #include <string>
42 : #include <type_traits>
43 : #include <vector>
44 :
45 : namespace monero {
46 : class monero_rpc_connection;
47 : }
48 :
49 : // helpers shared by the monero_* translation units. Not part of the public ABI
50 : namespace monero_c {
51 :
52 : // the monero-cpp connection behind a handle, shared with the daemons and wallets created from it
53 : std::shared_ptr<monero::monero_rpc_connection> connection_of(::monero_rpc_connection* connection);
54 :
55 : // makes the calling thread free, when it ends, what monero-project keeps for it. The first password hash of a thread, which is the one of a
56 : // wallet that is made, opened or given a new password, allocates a scratchpad of 2 MB for the thread, and only slow_hash_free_state() frees
57 : // it. monero-project calls that on its own threads, so a thread of the caller that has used a wallet and ends would keep the 2 MB until the
58 : // process ends. guard() calls it, so every thread that calls the library is covered
59 : void free_slow_hash_state_at_thread_exit() noexcept;
60 :
61 : // records msg as the calling thread's last error. It never throws: if the message can't be
62 : // stored because memory ran out, the last error is empty
63 : void set_last_error(const char* msg) noexcept;
64 :
65 : // records "<name> must not be null" as the last error, with the same guarantee
66 : void set_null_error(const char* name) noexcept;
67 :
68 : // returns the calling thread's last error, or "" if none
69 : const std::string& last_error();
70 :
71 30203 : inline std::string safe_str(const char* s) {
72 30203 : return s ? std::string(s) : std::string();
73 : }
74 :
75 : // the deepest nesting that a JSON argument may have. The parsers behind the arguments
76 : // recurse once per level, so a deeper document overflows the stack
77 : const size_t MAX_JSON_DEPTH = 64;
78 :
79 : // throws std::invalid_argument if json nests objects and arrays deeper than MAX_JSON_DEPTH
80 : void check_json_depth(const std::string& json);
81 :
82 : // the longest mnemonic that is checked. A valid one is a few hundred bytes, and monero-project
83 : // splits it in time quadratic in its length, so a megabyte of text would hold a call for minutes
84 : const size_t MAX_MNEMONIC_LENGTH = 4096;
85 :
86 : // throws std::invalid_argument if the mnemonic is longer than MAX_MNEMONIC_LENGTH
87 692 : inline void check_mnemonic_length(const std::string& mnemonic) {
88 692 : if (mnemonic.size() > MAX_MNEMONIC_LENGTH) throw std::invalid_argument("mnemonic is longer than " + std::to_string(MAX_MNEMONIC_LENGTH) + " bytes");
89 615 : }
90 :
91 : // the longest period of a poll or a sync that is accepted, in milliseconds, 24.8 days. monero-cpp waits for a period with a signed long,
92 : // which is 32 bits on Windows, so a period of 0 or one that doesn't fit makes the thread poll again at once, over and over, and with a
93 : // remote daemon that is a flood of requests
94 : const uint64_t MAX_PERIOD_MS = 2147483647;
95 :
96 : // throws std::invalid_argument if the period is 0 or longer than MAX_PERIOD_MS
97 41 : inline void check_period_ms(uint64_t period_ms) {
98 41 : if (period_ms == 0 || period_ms > MAX_PERIOD_MS) throw std::invalid_argument("period_ms must be between 1 and " + std::to_string(MAX_PERIOD_MS));
99 24 : }
100 :
101 : // mallocs at least 1 byte so a successful zero-length allocation is never
102 : // mistaken for malloc() failure (which is the only case this throws)
103 5122 : inline char* dup_string(const std::string& s) {
104 5122 : char* out = static_cast<char*>(std::malloc(s.size() + 1));
105 5122 : if (out == nullptr) throw std::bad_alloc();
106 5122 : std::copy(s.c_str(), s.c_str() + s.size() + 1, out);
107 5122 : return out;
108 : }
109 :
110 : // maps the C network enum to monero-cpp's, which has the same values except regtest. Regtest maps
111 : // to mainnet, and the caller keeps the flag. Returns false if the value is out of range
112 : template <class NetworkType>
113 5284 : bool to_network_type(int32_t network_type, NetworkType& out) {
114 5284 : if (network_type < MONERO_UTILS_NETWORK_MAINNET || network_type > MONERO_UTILS_NETWORK_REGTEST) return false;
115 4812 : out = network_type == MONERO_UTILS_NETWORK_REGTEST ? NetworkType::MAINNET : static_cast<NetworkType>(network_type);
116 4812 : return true;
117 : }
118 :
119 : // like to_network_type(), but throws if the value is out of range
120 : template <class NetworkType>
121 3016 : NetworkType network_of(int32_t network_type) {
122 3016 : NetworkType out = NetworkType::MAINNET;
123 3016 : if (!to_network_type(network_type, out)) throw std::invalid_argument("unknown network type");
124 2545 : return out;
125 : }
126 :
127 : // runs fn, converting any thrown exception into MONERO_ERROR + last_error
128 : template <class F>
129 27596 : monero_result guard(F&& fn) {
130 27596 : free_slow_hash_state_at_thread_exit();
131 : try {
132 27596 : fn();
133 9983 : return MONERO_OK;
134 17613 : } catch (const std::exception& e) {
135 17613 : set_last_error(e.what());
136 17613 : return MONERO_ERROR;
137 0 : } catch (...) {
138 0 : set_last_error("unknown error");
139 0 : return MONERO_ERROR;
140 : }
141 : }
142 :
143 : // checks a required pointer. On failure, sets last_error to "<name> must not be null"
144 55717 : inline bool require(const void* ptr, const char* name) {
145 55717 : if (ptr != nullptr) return true;
146 2806 : set_null_error(name);
147 2806 : return false;
148 : }
149 :
150 : // sets an output to NULL (a count to 0), unless the pointer to it is NULL. Done before the
151 : // arguments are checked, so a failed call never leaves a stale pointer for the caller to free
152 : template <class T>
153 20864 : void reset_out(T* out) {
154 20864 : if (out != nullptr) *out = T();
155 20864 : }
156 :
157 : // copies a C array. Throws if it is NULL and the count is not zero. The name is the argument,
158 : // for the error message
159 : template <class T>
160 140 : std::vector<T> array_of(const T* items, size_t count, const char* name) {
161 140 : if (count == 0) return std::vector<T>();
162 111 : if (items == nullptr) throw std::invalid_argument(std::string(name) + " must not be null");
163 83 : return std::vector<T>(items, items + count);
164 : }
165 :
166 : // copies a C array of strings. Throws if the array is NULL and the count is not zero, or if
167 : // an element is NULL
168 1080 : inline std::vector<std::string> string_array(const char* const* items, size_t count, const char* name) {
169 1080 : std::vector<std::string> out;
170 1080 : if (count == 0) return out;
171 1037 : if (items == nullptr) throw std::invalid_argument(std::string(name) + " must not be null");
172 2467 : for (size_t i = 0; i < count; i++) {
173 1516 : if (items[i] == nullptr) throw std::invalid_argument(std::string(name) + " must not contain NULL");
174 1474 : out.push_back(items[i]);
175 : }
176 951 : return out;
177 86 : }
178 :
179 : // maps an optional argument (NULL means none) to boost::optional
180 : template <class T>
181 400 : boost::optional<T> optional_of(const T* value) {
182 400 : boost::optional<T> out;
183 400 : if (value != nullptr) out = *value;
184 400 : return out;
185 : }
186 :
187 : // copies binary data into a new buffer that monero_utils_free() releases. The buffer is
188 : // never NULL, so an empty result is still a valid pointer
189 1503 : inline uint8_t* dup_buffer(const std::string& bin, size_t* out_len) {
190 1503 : uint8_t* out = static_cast<uint8_t*>(std::malloc(bin.empty() ? 1 : bin.size()));
191 1503 : if (out == nullptr) throw std::bad_alloc();
192 1503 : std::copy(bin.begin(), bin.end(), out);
193 1503 : *out_len = bin.size();
194 1503 : return out;
195 : }
196 :
197 : // copies items into a new array that free() releases with monero_utils_free(). The
198 : // allocation is never NULL, so an empty result is still a valid pointer
199 : template <class T>
200 256 : T* dup_array(const std::vector<T>& items, size_t* out_count) {
201 : static_assert(std::is_trivially_copyable<T>::value, "dup_array assigns into malloc'd memory, so T must be trivially copyable");
202 256 : T* out = static_cast<T*>(std::malloc((items.empty() ? 1 : items.size()) * sizeof(T)));
203 256 : if (out == nullptr) throw std::bad_alloc();
204 1059 : for (size_t i = 0; i < items.size(); i++) out[i] = items[i];
205 256 : *out_count = items.size();
206 256 : return out;
207 : }
208 :
209 : // serializes a list of monero-cpp objects as a JSON array. Null items stay null
210 : template <class T>
211 148 : std::string json_of_list(const std::vector<std::shared_ptr<T>>& items) {
212 148 : std::string json = "[";
213 736 : for (size_t i = 0; i < items.size(); i++) {
214 588 : if (i > 0) json += ",";
215 588 : json += items[i] ? items[i]->serialize() : std::string("null");
216 : }
217 296 : return json + "]";
218 148 : }
219 :
220 : // serializes a list of monero-cpp objects that are held by value
221 : template <class T>
222 192 : std::string json_of_list(const std::vector<T>& items) {
223 192 : std::string json = "[";
224 392 : for (size_t i = 0; i < items.size(); i++) {
225 200 : if (i > 0) json += ",";
226 200 : json += items[i].serialize();
227 : }
228 384 : return json + "]";
229 192 : }
230 :
231 : // serializes a JSON array of strings, e.g. hashes or hexes
232 37 : inline std::string json_of_strings(const std::vector<std::string>& items) {
233 37 : rapidjson::StringBuffer buffer;
234 37 : rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
235 37 : writer.StartArray();
236 109 : for (const auto& item : items) writer.String(item.c_str(), static_cast<rapidjson::SizeType>(item.size()));
237 37 : writer.EndArray();
238 74 : return buffer.GetString();
239 37 : }
240 :
241 : } // namespace monero_c
242 :
243 : #endif // MONERO_C_COMMON_H
|