xrpld
Loading...
Searching...
No Matches
STIssue_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/amount.h> // IWYU pragma: keep
5#include <test/jtx/envconfig.h>
6
7#include <xrpld/core/Config.h>
8
9#include <xrpl/basics/base_uint.h>
10#include <xrpl/basics/strHex.h>
11#include <xrpl/beast/unit_test/suite.h>
12#include <xrpl/json/json_value.h>
13#include <xrpl/json/to_string.h>
14#include <xrpl/protocol/AccountID.h>
15#include <xrpl/protocol/Indexes.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/MPTIssue.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/STIssue.h>
20#include <xrpl/protocol/Serializer.h>
21#include <xrpl/protocol/UintTypes.h>
22#include <xrpl/protocol/jss.h>
23
24#include <array>
25#include <cstdint>
26#include <memory>
27
28namespace xrpl::test {
29
31{
32public:
33 void
35 {
36 testcase("Constructor");
37 using namespace jtx;
38 Account const alice{"alice"};
39 auto const usd = alice["USD"];
40 Issue issue;
41
42 try
43 {
44 issue = xrpIssue();
45 issue.account = alice;
46 STIssue const stissue(sfAsset, Asset{issue});
47 fail("Inconsistent XRP Issue doesn't fail");
48 }
49 catch (...)
50 {
51 pass();
52 }
53
54 try
55 {
56 issue = usd;
57 issue.account = xrpAccount();
58 STIssue const stissue(sfAsset, Asset{issue});
59 fail("Inconsistent IOU Issue doesn't fail");
60 }
61 catch (...)
62 {
63 pass();
64 }
65
66 try
67 {
68 // Currency is USD but account is XRP
69 auto const data =
70 "00000000000000000000000055534400000000000000000000000000000000"
71 "000000000000000000";
72 BaseUInt<320> uint;
73 (void)uint.parseHex(data);
74 SerialIter iter(Slice(uint.data(), uint.size()));
75 STIssue const stissue(iter, sfAsset);
76 fail("Inconsistent IOU Issue doesn't fail on serializer");
77 }
78 catch (...)
79 {
80 pass();
81 }
82
83 try
84 {
85 STIssue const stissue(sfAsset, Asset{xrpIssue()});
86 }
87 catch (...)
88 {
89 fail("XRP issue failed");
90 }
91
92 try
93 {
94 STIssue const stissue(sfAsset, Asset{usd});
95 }
96 catch (...)
97 {
98 fail("USD issue failed");
99 }
100
101 try
102 {
103 auto const data =
104 "0000000000000000000000005553440000000000ae123a8556f3cf91154711"
105 "376afb0f894f832b3d";
106 BaseUInt<320> uint;
107 (void)uint.parseHex(data);
108 SerialIter iter(Slice(uint.data(), uint.size()));
109 STIssue const stissue(iter, sfAsset);
110 BEAST_EXPECT(stissue.value() == usd);
111 }
112 catch (...)
113 {
114 fail("USD Issue fails on serializer");
115 }
116
117 try
118 {
119 auto const data = "0000000000000000000000000000000000000000";
120 BaseUInt<160> uint;
121 (void)uint.parseHex(data);
122 SerialIter iter(Slice(uint.data(), uint.size()));
123 STIssue const stissue(iter, sfAsset);
124 BEAST_EXPECT(stissue.value() == xrpCurrency());
125 }
126 catch (...)
127 {
128 fail("XRP Issue fails on serializer");
129 }
130 }
131
132 void
134 {
135 testcase("Compare");
136 using namespace jtx;
137 Account const alice{"alice"};
138 auto const usd = alice["USD"];
139 Asset const asset1{xrpIssue()};
140 Asset const asset2{usd};
141 Asset const asset3{MPTID{2}};
142
143 BEAST_EXPECT(STIssue(sfAsset, asset1) != asset2);
144 BEAST_EXPECT(STIssue(sfAsset, asset1) != asset3);
145 BEAST_EXPECT(STIssue(sfAsset, asset1) == asset1);
146 BEAST_EXPECT(STIssue(sfAsset, asset1).getText() == "XRP");
147 BEAST_EXPECT(
148 STIssue(sfAsset, asset2).getText() == "USD/rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn");
149 BEAST_EXPECT(
150 STIssue(sfAsset, asset3).getText() ==
151 "000000000000000000000000000000000000000000000002");
152 }
153
154 void
156 {
157 testcase("noAccount issuer rejected via RPC sign");
158
159 using namespace jtx;
160 Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
161 cfg->loadFromString("[signing_support]\ntrue");
162 return cfg;
163 })};
164
165 Account const alice{"alice"};
166 env.fund(XRP(10000), alice);
167 env.close();
168
169 json::Value txJson;
170 txJson[jss::TransactionType] = "AMMDelete";
171 txJson[jss::Account] = alice.human();
172 txJson[jss::Asset][jss::currency] = "USD";
173 txJson[jss::Asset][jss::issuer] = to_string(noAccount());
174 txJson[jss::Asset2][jss::currency] = "XRP";
175
176 json::Value req;
177 req[jss::tx_json] = txJson;
178 req[jss::secret] = alice.name();
179
180 auto const result = env.rpc("json", "sign", to_string(req))[jss::result];
181
182 BEAST_EXPECT(result[jss::error] == "invalidParams");
183 BEAST_EXPECT(result[jss::error_message] == "Field 'tx_json.Asset' has invalid data.");
184 }
185
186 void
188 {
189 testcase("noAccount issuer rejection");
190
191 {
192 json::Value jv;
193 jv[jss::currency] = "USD";
194 jv[jss::issuer] = to_string(noAccount());
195
196 try
197 {
198 issueFromJson(sfAsset, jv);
199 fail("issueFromJson accepted noAccount() as IOU issuer");
200 }
201 catch (...)
202 {
203 pass();
204 }
205 }
206
207 {
208 Serializer s;
209 s.addBitString(toCurrency("USD"));
211 SerialIter iter(s.slice());
212
213 try
214 {
215 STIssue const stissue(iter, sfAsset);
216 fail(
217 "STIssue deserialization of [USD][noAccount()] should "
218 "throw");
219 }
220 catch (...)
221 {
222 pass();
223 }
224 }
225 }
226
227 void
229 {
230 testcase("xrpAccount issuer rejected via RPC sign");
231
232 using namespace jtx;
233 Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
234 cfg->loadFromString("[signing_support]\ntrue");
235 return cfg;
236 })};
237
238 Account const alice{"alice"};
239 env.fund(XRP(10000), alice);
240 env.close();
241
242 json::Value txJson;
243 txJson[jss::TransactionType] = "AMMDelete";
244 txJson[jss::Account] = alice.human();
245 txJson[jss::Asset][jss::currency] = "USD";
246 txJson[jss::Asset][jss::issuer] = to_string(xrpAccount());
247 txJson[jss::Asset2][jss::currency] = "XRP";
248
249 json::Value req;
250 req[jss::tx_json] = txJson;
251 req[jss::secret] = alice.name();
252
253 auto const result = env.rpc("json", "sign", to_string(req))[jss::result];
254
255 BEAST_EXPECT(result[jss::error] == "invalidParams");
256 BEAST_EXPECT(result[jss::error_message] == "Field 'tx_json.Asset' has invalid data.");
257 }
258
259 void
261 {
262 testcase("xrpAccount issuer rejection");
263
264 {
265 json::Value jv;
266 jv[jss::currency] = "USD";
267 jv[jss::issuer] = to_string(xrpAccount());
268
269 try
270 {
271 issueFromJson(sfAsset, jv);
272 fail("issueFromJson accepted xrpAccount() as IOU issuer");
273 }
274 catch (...)
275 {
276 pass();
277 }
278 }
279 }
280
281 void
283 {
284 testcase("MPT serialization");
285 using namespace jtx;
286 Account const alice{"alice"};
287
288 // 0x01020304 pins canonical MPTID bytes 01 02 03 04 and
289 // preserved STIssue wire bytes 04 03 02 01 on BE and LE.
290 auto const sequences = std::to_array<std::uint32_t>({0x00000001, 0x01020304, 0xa1b2c3d4});
291
292 for (auto const vector : sequences)
293 {
294 MPTID const mptID = makeMptID(vector, alice);
295 MPTIssue const issue{mptID};
296 STIssue const stIssue(sfAsset, Asset{issue});
297
298 Serializer actual;
299 stIssue.add(actual);
300
301 // STIssue preserves the existing little-endian validator ledger bytes.
302 Serializer expected;
303 expected.addBitString(alice.id());
304 expected.addBitString(noAccount());
305 {
307 static_cast<unsigned char>(vector),
308 static_cast<unsigned char>(vector >> 8),
309 static_cast<unsigned char>(vector >> 16),
310 static_cast<unsigned char>(vector >> 24)};
311 expected.addRaw(bytes.data(), bytes.size());
312 }
313
314 BEAST_EXPECTS(strHex(actual) == strHex(expected), strHex(actual));
315
316 // Decoding the preserved wire format must recover the canonical MPTID.
317 SerialIter iter(expected.slice());
318 STIssue const decoded(iter, sfAsset);
319 BEAST_EXPECT(decoded.holds<MPTIssue>());
320 BEAST_EXPECT(decoded.value().get<MPTIssue>().getMptID() == mptID);
321
322 // A decoded ledger value must serialize back to the same bytes.
323 Serializer roundTrip;
324 decoded.add(roundTrip);
325 BEAST_EXPECTS(strHex(roundTrip) == strHex(expected), strHex(roundTrip));
326 }
327 }
328
329 void
330 run() override
331 {
332 // compliments other unit tests to ensure complete coverage
334 testCompare();
340 }
341};
342
344
345} // namespace xrpl::test
A testsuite class.
Definition suite.h:52
void pass()
Record a successful test condition.
Definition suite.h:532
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:554
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Represents a JSON value.
Definition json_value.h:117
constexpr TIss const & get() const
Integers of any length that is a multiple of 32-bits.
Definition base_uint.h:82
pointer data()
Definition base_uint.h:117
static constexpr std::size_t size()
Definition base_uint.h:548
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:525
A currency issued by an account.
Definition Issue.h:18
AccountID account
Definition Issue.h:21
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:43
void add(Serializer &s) const override
Definition STIssue.cpp:96
bool holds() const
Definition STIssue.h:105
value_type const & value() const noexcept
Definition STIssue.h:120
int addBitString(BaseUInt< Bits, Tag > const &v)
Definition Serializer.h:106
int addRaw(Blob const &vector)
Slice slice() const noexcept
Definition Serializer.h:45
An immutable linear range of bytes.
Definition Slice.h:28
void run() override
Runs the suite.
Immutable cryptographic account descriptor.
Definition jtx/Account.h:21
std::string const & human() const
Returns the human readable public key.
std::string const & name() const
Return the name.
Definition jtx/Account.h:75
AccountID id() const
Returns the Account ID.
A transaction testing environment.
Definition Env.h:161
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:323
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:1056
T data(T... args)
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:37
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:108
Issue issueFromJson(json::Value const &v)
Definition Issue.cpp:89
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:65
Currency const & xrpCurrency()
XRP currency.
Definition UintTypes.cpp:99
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:54
MPTID makeMptID(std::uint32_t const sequence, AccountID const &account)
Definition Indexes.cpp:184
AccountID const & noAccount()
A placeholder for empty accounts.
AccountID const & xrpAccount()
Compute AccountID from public key.
T size(T... args)