rippled
DID_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/basics/strHex.h>
21 #include <ripple/protocol/Feature.h>
22 #include <ripple/protocol/Indexes.h>
23 #include <ripple/protocol/TxFlags.h>
24 #include <ripple/protocol/jss.h>
25 #include <test/jtx.h>
26 
27 #include <algorithm>
28 #include <iterator>
29 
30 namespace ripple {
31 namespace test {
32 
33 // Helper function that returns the owner count of an account root.
36 {
37  std::uint32_t ret{0};
38  if (auto const sleAcct = env.le(acct))
39  ret = sleAcct->at(sfOwnerCount);
40  return ret;
41 }
42 
43 bool
44 checkVL(Slice const& result, std::string expected)
45 {
46  Serializer s;
47  s.addRaw(result);
48  return s.getString() == expected;
49 }
50 
51 struct DID_test : public beast::unit_test::suite
52 {
53  void
55  {
56  testcase("featureDID Enabled");
57 
58  using namespace jtx;
59  // If the DID amendment is not enabled, you should not be able
60  // to set or delete DIDs.
61  Env env{*this, features - featureDID};
62  Account const alice{"alice"};
63  env.fund(XRP(5000), alice);
64  env.close();
65 
66  BEAST_EXPECT(ownerCount(env, alice) == 0);
67  env(did::setValid(alice), ter(temDISABLED));
68  env.close();
69 
70  BEAST_EXPECT(ownerCount(env, alice) == 0);
71  env(did::del(alice), ter(temDISABLED));
72  env.close();
73  }
74 
75  void
77  {
78  // Verify that the reserve behaves as expected for minting.
79  testcase("DID Account Reserve");
80 
81  using namespace test::jtx;
82 
83  Env env{*this, features};
84  Account const alice{"alice"};
85 
86  // Fund alice enough to exist, but not enough to meet
87  // the reserve for creating a DID.
88  auto const acctReserve = env.current()->fees().accountReserve(0);
89  auto const incReserve = env.current()->fees().increment;
90  env.fund(acctReserve, alice);
91  env.close();
92  BEAST_EXPECT(env.balance(alice) == acctReserve);
93  BEAST_EXPECT(ownerCount(env, alice) == 0);
94 
95  // alice does not have enough XRP to cover the reserve for a DID
96  env(did::setValid(alice), ter(tecINSUFFICIENT_RESERVE));
97  env.close();
98  BEAST_EXPECT(ownerCount(env, alice) == 0);
99 
100  // Pay alice almost enough to make the reserve for a DID.
101  env(pay(env.master, alice, incReserve + drops(19)));
102  BEAST_EXPECT(env.balance(alice) == acctReserve + incReserve + drops(9));
103  env.close();
104 
105  // alice still does not have enough XRP for the reserve of a DID.
106  env(did::setValid(alice), ter(tecINSUFFICIENT_RESERVE));
107  env.close();
108  BEAST_EXPECT(ownerCount(env, alice) == 0);
109 
110  // Pay alice enough to make the reserve for a DID.
111  env(pay(env.master, alice, drops(11)));
112  env.close();
113 
114  // Now alice can create a DID.
115  env(did::setValid(alice));
116  env.close();
117  BEAST_EXPECT(ownerCount(env, alice) == 1);
118 
119  // alice deletes her DID.
120  env(did::del(alice));
121  BEAST_EXPECT(ownerCount(env, alice) == 0);
122  env.close();
123  }
124 
125  void
127  {
128  testcase("Invalid DIDSet");
129 
130  using namespace jtx;
131  using namespace std::chrono;
132 
133  Env env(*this);
134  Account const alice{"alice"};
135  env.fund(XRP(5000), alice);
136  env.close();
137 
138  //----------------------------------------------------------------------
139  // preflight
140 
141  // invalid flags
142  BEAST_EXPECT(ownerCount(env, alice) == 0);
143  env(did::setValid(alice), txflags(0x00010000), ter(temINVALID_FLAG));
144  env.close();
145  BEAST_EXPECT(ownerCount(env, alice) == 0);
146 
147  // no fields
148  env(did::set(alice), ter(temEMPTY_DID));
149  env.close();
150  BEAST_EXPECT(ownerCount(env, alice) == 0);
151 
152  // all empty fields
153  env(did::set(alice),
154  did::uri(""),
155  did::document(""),
156  did::data(""),
157  ter(temEMPTY_DID));
158  env.close();
159  BEAST_EXPECT(ownerCount(env, alice) == 0);
160 
161  // uri is too long
162  const std::string longString(257, 'a');
163  env(did::set(alice), did::uri(longString), ter(temMALFORMED));
164  env.close();
165  BEAST_EXPECT(ownerCount(env, alice) == 0);
166 
167  // document is too long
168  env(did::set(alice), did::document(longString), ter(temMALFORMED));
169  env.close();
170  BEAST_EXPECT(ownerCount(env, alice) == 0);
171 
172  // attestation is too long
173  env(did::set(alice),
174  did::document("data"),
175  did::data(longString),
176  ter(temMALFORMED));
177  env.close();
178  BEAST_EXPECT(ownerCount(env, alice) == 0);
179 
180  // Modifying a DID to become empty is checked in testSetModify
181  }
182 
183  void
185  {
186  testcase("Invalid DIDDelete");
187 
188  using namespace jtx;
189  using namespace std::chrono;
190 
191  Env env(*this);
192  Account const alice{"alice"};
193  env.fund(XRP(5000), alice);
194  env.close();
195 
196  //----------------------------------------------------------------------
197  // preflight
198 
199  // invalid flags
200  BEAST_EXPECT(ownerCount(env, alice) == 0);
201  env(did::del(alice), txflags(0x00010000), ter(temINVALID_FLAG));
202  env.close();
203  BEAST_EXPECT(ownerCount(env, alice) == 0);
204 
205  //----------------------------------------------------------------------
206  // doApply
207 
208  // DID doesn't exist
209  env(did::del(alice), ter(tecNO_ENTRY));
210  env.close();
211  BEAST_EXPECT(ownerCount(env, alice) == 0);
212  }
213 
214  void
216  {
217  testcase("Valid Initial DIDSet");
218 
219  using namespace jtx;
220  using namespace std::chrono;
221 
222  Env env(*this);
223  Account const alice{"alice"};
224  Account const bob{"bob"};
225  Account const charlie{"charlie"};
226  Account const dave{"dave"};
227  Account const edna{"edna"};
228  Account const francis{"francis"};
229  Account const george{"george"};
230  env.fund(XRP(5000), alice, bob, charlie, dave, edna, francis);
231  env.close();
232  BEAST_EXPECT(ownerCount(env, alice) == 0);
233  BEAST_EXPECT(ownerCount(env, bob) == 0);
234  BEAST_EXPECT(ownerCount(env, charlie) == 0);
235 
236  // only URI
237  env(did::set(alice), did::uri("uri"));
238  BEAST_EXPECT(ownerCount(env, alice) == 1);
239 
240  // only DIDDocument
241  env(did::set(bob), did::document("data"));
242  BEAST_EXPECT(ownerCount(env, bob) == 1);
243 
244  // only Data
245  env(did::set(charlie), did::data("data"));
246  BEAST_EXPECT(ownerCount(env, charlie) == 1);
247 
248  // URI + Data
249  env(did::set(dave), did::uri("uri"), did::data("attest"));
250  BEAST_EXPECT(ownerCount(env, dave) == 1);
251 
252  // URI + DIDDocument
253  env(did::set(edna), did::uri("uri"), did::document("data"));
254  BEAST_EXPECT(ownerCount(env, edna) == 1);
255 
256  // DIDDocument + Data
257  env(did::set(francis), did::document("data"), did::data("attest"));
258  BEAST_EXPECT(ownerCount(env, francis) == 1);
259 
260  // URI + DIDDocument + Data
261  env(did::set(george),
262  did::uri("uri"),
263  did::document("data"),
264  did::data("attest"));
265  BEAST_EXPECT(ownerCount(env, george) == 1);
266  }
267 
268  void
270  {
271  testcase("Modify DID with DIDSet");
272 
273  using namespace jtx;
274  using namespace std::chrono;
275 
276  Env env(*this);
277  Account const alice{"alice"};
278  env.fund(XRP(5000), alice);
279  env.close();
280  BEAST_EXPECT(ownerCount(env, alice) == 0);
281  auto const ar = env.le(alice);
282 
283  // Create DID
284  std::string const initialURI = "uri";
285  {
286  env(did::set(alice), did::uri(initialURI));
287  BEAST_EXPECT(ownerCount(env, alice) == 1);
288  auto const sleDID = env.le(keylet::did(alice.id()));
289  BEAST_EXPECT(sleDID);
290  BEAST_EXPECT(checkVL((*sleDID)[sfURI], initialURI));
291  BEAST_EXPECT(!sleDID->isFieldPresent(sfDIDDocument));
292  BEAST_EXPECT(!sleDID->isFieldPresent(sfData));
293  }
294 
295  // Try to delete URI, fails because no elements are set
296  {
297  env(did::set(alice), did::uri(""), ter(tecEMPTY_DID));
298  BEAST_EXPECT(ownerCount(env, alice) == 1);
299  auto const sleDID = env.le(keylet::did(alice.id()));
300  BEAST_EXPECT(checkVL((*sleDID)[sfURI], initialURI));
301  BEAST_EXPECT(!sleDID->isFieldPresent(sfDIDDocument));
302  BEAST_EXPECT(!sleDID->isFieldPresent(sfData));
303  }
304 
305  // Set DIDDocument
306  std::string const initialDocument = "data";
307  {
308  env(did::set(alice), did::document(initialDocument));
309  BEAST_EXPECT(ownerCount(env, alice) == 1);
310  auto const sleDID = env.le(keylet::did(alice.id()));
311  BEAST_EXPECT(checkVL((*sleDID)[sfURI], initialURI));
312  BEAST_EXPECT(checkVL((*sleDID)[sfDIDDocument], initialDocument));
313  BEAST_EXPECT(!sleDID->isFieldPresent(sfData));
314  }
315 
316  // Set Data
317  std::string const initialData = "attest";
318  {
319  env(did::set(alice), did::data(initialData));
320  BEAST_EXPECT(ownerCount(env, alice) == 1);
321  auto const sleDID = env.le(keylet::did(alice.id()));
322  BEAST_EXPECT(checkVL((*sleDID)[sfURI], initialURI));
323  BEAST_EXPECT(checkVL((*sleDID)[sfDIDDocument], initialDocument));
324  BEAST_EXPECT(checkVL((*sleDID)[sfData], initialData));
325  }
326 
327  // Remove URI
328  {
329  env(did::set(alice), did::uri(""));
330  BEAST_EXPECT(ownerCount(env, alice) == 1);
331  auto const sleDID = env.le(keylet::did(alice.id()));
332  BEAST_EXPECT(!sleDID->isFieldPresent(sfURI));
333  BEAST_EXPECT(checkVL((*sleDID)[sfDIDDocument], initialDocument));
334  BEAST_EXPECT(checkVL((*sleDID)[sfData], initialData));
335  }
336 
337  // Remove Data
338  {
339  env(did::set(alice), did::data(""));
340  BEAST_EXPECT(ownerCount(env, alice) == 1);
341  auto const sleDID = env.le(keylet::did(alice.id()));
342  BEAST_EXPECT(!sleDID->isFieldPresent(sfURI));
343  BEAST_EXPECT(checkVL((*sleDID)[sfDIDDocument], initialDocument));
344  BEAST_EXPECT(!sleDID->isFieldPresent(sfData));
345  }
346 
347  // Remove Data + set URI
348  std::string const secondURI = "uri2";
349  {
350  env(did::set(alice), did::uri(secondURI), did::document(""));
351  BEAST_EXPECT(ownerCount(env, alice) == 1);
352  auto const sleDID = env.le(keylet::did(alice.id()));
353  BEAST_EXPECT(checkVL((*sleDID)[sfURI], secondURI));
354  BEAST_EXPECT(!sleDID->isFieldPresent(sfDIDDocument));
355  BEAST_EXPECT(!sleDID->isFieldPresent(sfData));
356  }
357 
358  // Remove URI + set DIDDocument
359  std::string const secondDocument = "data2";
360  {
361  env(did::set(alice), did::uri(""), did::document(secondDocument));
362  BEAST_EXPECT(ownerCount(env, alice) == 1);
363  auto const sleDID = env.le(keylet::did(alice.id()));
364  BEAST_EXPECT(!sleDID->isFieldPresent(sfURI));
365  BEAST_EXPECT(checkVL((*sleDID)[sfDIDDocument], secondDocument));
366  BEAST_EXPECT(!sleDID->isFieldPresent(sfData));
367  }
368 
369  // Remove DIDDocument + set Data
370  std::string const secondData = "randomData";
371  {
372  env(did::set(alice), did::document(""), did::data(secondData));
373  BEAST_EXPECT(ownerCount(env, alice) == 1);
374  auto const sleDID = env.le(keylet::did(alice.id()));
375  BEAST_EXPECT(!sleDID->isFieldPresent(sfURI));
376  BEAST_EXPECT(!sleDID->isFieldPresent(sfDIDDocument));
377  BEAST_EXPECT(checkVL((*sleDID)[sfData], secondData));
378  }
379 
380  // Delete DID
381  {
382  env(did::del(alice));
383  BEAST_EXPECT(ownerCount(env, alice) == 0);
384  auto const sleDID = env.le(keylet::did(alice.id()));
385  BEAST_EXPECT(!sleDID);
386  }
387  }
388 
389  void
390  run() override
391  {
392  using namespace test::jtx;
393  FeatureBitset const all{
395  testEnabled(all);
400  }
401 };
402 
403 BEAST_DEFINE_TESTSUITE(DID, app, ripple);
404 
405 } // namespace test
406 } // namespace ripple
ripple::sfOwnerCount
const SF_UINT32 sfOwnerCount
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
ripple::test::DID_test::testEnabled
void testEnabled(FeatureBitset features)
Definition: DID_test.cpp:54
std::string
STL class.
ripple::test::DID_test::testSetModify
void testSetModify(FeatureBitset features)
Definition: DID_test.cpp:269
ripple::test::jtx::drops
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Definition: amount.h:241
ripple::featureDID
const uint256 featureDID
ripple::test::jtx::ter
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:33
ripple::test::jtx::did::uri
Sets the optional URI on a DIDSet.
Definition: did.h:59
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
ripple::TxSearched::all
@ all
ripple::Serializer::getString
std::string getString() const
Definition: Serializer.h:205
ripple::test::jtx::did::document
Sets the optional DIDDocument on a DIDSet.
Definition: did.h:41
iterator
ripple::keylet::did
Keylet did(AccountID const &account) noexcept
Definition: Indexes.cpp:442
ripple::temEMPTY_DID
@ temEMPTY_DID
Definition: TER.h:137
ripple::test::ownerCount
std::uint32_t ownerCount(test::jtx::Env const &env, test::jtx::Account const &acct)
Definition: DID_test.cpp:35
ripple::test::DID_test::run
void run() override
Definition: DID_test.cpp:390
algorithm
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:110
ripple::test::DID_test::testSetValidInitial
void testSetValidInitial(FeatureBitset features)
Definition: DID_test.cpp:215
ripple::test::DID_test::testSetInvalid
void testSetInvalid(FeatureBitset features)
Definition: DID_test.cpp:126
ripple::Serializer::addRaw
int addRaw(Blob const &vector)
Definition: Serializer.cpp:100
ripple::test::jtx::did::data
Sets the optional Attestation on a DIDSet.
Definition: did.h:77
ripple::test::jtx::txflags
Set the flags on a JTx.
Definition: txflags.h:30
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::test::jtx::supported_amendments
FeatureBitset supported_amendments()
Definition: Env.h:71
std::uint32_t
ripple::test::DID_test::testAccountReserve
void testAccountReserve(FeatureBitset features)
Definition: DID_test.cpp:76
ripple::test::DID_test::testDeleteInvalid
void testDeleteInvalid(FeatureBitset features)
Definition: DID_test.cpp:184
ripple::Serializer
Definition: Serializer.h:40
ripple::sfURI
const SF_VL sfURI
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::DID_test
Definition: DID_test.cpp:51
ripple::sfDIDDocument
const SF_VL sfDIDDocument
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:113
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:228
ripple::test::jtx::Env::le
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:216
ripple::tecEMPTY_DID
@ tecEMPTY_DID
Definition: TER.h:334
ripple::sfData
const SF_VL sfData
ripple::FeatureBitset
Definition: Feature.h:113
ripple::tecINSUFFICIENT_RESERVE
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:288
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::tecNO_ENTRY
@ tecNO_ENTRY
Definition: TER.h:287
ripple::temMALFORMED
@ temMALFORMED
Definition: TER.h:86
ripple::test::checkVL
bool checkVL(Slice const &result, std::string expected)
Definition: DID_test.cpp:44
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:117
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)
std::chrono