Add support for XLS-81 Permissioned DEX (#5404)

Modified transactions:
- OfferCreate
- Payment

Modified RPCs:
- book_changes
- subscribe
- book_offers
- ripple_path_find
- path_find

Spec: https://github.com/XRPLF/XRPL-Standards/pull/281
This commit is contained in:
Shawn Xie
2025-05-30 13:24:48 -04:00
committed by GitHub
parent e0bc3dd51f
commit 0a34b5c691
73 changed files with 4591 additions and 308 deletions

View File

@@ -22,7 +22,10 @@
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/Issue.h>
#include <sys/types.h>
#include <map>
#include <optional>
#include <set>
#include <typeinfo>
#include <unordered_set>
@@ -46,6 +49,8 @@ namespace ripple {
class Issue_test : public beast::unit_test::suite
{
public:
using Domain = uint256;
// Comparison, hash tests for uint60 (via base_uint)
template <typename Unsigned>
void
@@ -239,6 +244,120 @@ public:
}
}
template <class Set>
void
testIssueDomainSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Set c;
c.insert(std::make_pair(a1, domain1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(a2, domain1));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(a2, domain2));
if (!BEAST_EXPECT(c.size() == 3))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
template <class Map>
void
testIssueDomainMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Map c;
c.insert(std::make_pair(std::make_pair(a1, domain1), 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(std::make_pair(a2, domain1), 2));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(std::make_pair(a2, domain2), 2));
if (!BEAST_EXPECT(c.size() == 3))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
void
testIssueDomainSets()
{
testcase("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
testcase("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
testcase("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
testcase("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
}
void
testIssueDomainMaps()
{
testcase("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
testcase("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
testcase("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
testcase("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
testcase("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
#endif
}
void
testIssueSets()
{
@@ -306,15 +425,88 @@ public:
Issue a2(c1, i2);
Issue a3(c2, i2);
Issue a4(c3, i2);
uint256 const domain1{1};
uint256 const domain2{2};
BEAST_EXPECT(Book(a1, a2) != Book(a2, a3));
BEAST_EXPECT(Book(a1, a2) < Book(a2, a3));
BEAST_EXPECT(Book(a1, a2) <= Book(a2, a3));
BEAST_EXPECT(Book(a2, a3) <= Book(a2, a3));
BEAST_EXPECT(Book(a2, a3) == Book(a2, a3));
BEAST_EXPECT(Book(a2, a3) >= Book(a2, a3));
BEAST_EXPECT(Book(a3, a4) >= Book(a2, a3));
BEAST_EXPECT(Book(a3, a4) > Book(a2, a3));
// Books without domains
BEAST_EXPECT(Book(a1, a2, std::nullopt) != Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a1, a2, std::nullopt) < Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a1, a2, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a3, a4, std::nullopt) >= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a3, a4, std::nullopt) > Book(a2, a3, std::nullopt));
// test domain books
{
// Books with different domains
BEAST_EXPECT(Book(a2, a3, domain1) != Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) > Book(a2, a3, domain1));
// One Book has a domain, the other does not
BEAST_EXPECT(Book(a2, a3, domain1) != Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
// Both Books have the same domain
BEAST_EXPECT(Book(a2, a3, domain1) == Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain2) == Book(a2, a3, domain2));
BEAST_EXPECT(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Both Books have no domain
BEAST_EXPECT(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Testing comparisons with >= and <=
// When comparing books with domain1 vs domain2
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain2) <= Book(a2, a3, domain2));
// One Book has domain1 and the other has no domain
BEAST_EXPECT(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
// One Book has domain2 and the other has no domain
BEAST_EXPECT(Book(a2, a3, domain2) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain2));
// Comparing two Books with no domains
BEAST_EXPECT(
Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(
Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
// Test case where domain1 is less than domain2
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
// Test case where domain2 is equal to domain1
BEAST_EXPECT(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain1));
// More test cases involving a4 (with domain2)
// Comparing Book with domain2 (a4) to a Book with domain1
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a3, a4, domain2));
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1));
// Comparing Book with domain2 (a4) to a Book with no domain
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with the same domain
BEAST_EXPECT(Book(a3, a4, domain2) == Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with domain1
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a3, a4, domain2));
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1));
}
std::hash<Book> hash;
@@ -336,18 +528,99 @@ public:
// log << std::hex << hash (Book (a3, a4));
// log << std::hex << hash (Book (a3, a4));
BEAST_EXPECT(hash(Book(a1, a2)) == hash(Book(a1, a2)));
BEAST_EXPECT(hash(Book(a1, a3)) == hash(Book(a1, a3)));
BEAST_EXPECT(hash(Book(a1, a4)) == hash(Book(a1, a4)));
BEAST_EXPECT(hash(Book(a2, a3)) == hash(Book(a2, a3)));
BEAST_EXPECT(hash(Book(a2, a4)) == hash(Book(a2, a4)));
BEAST_EXPECT(hash(Book(a3, a4)) == hash(Book(a3, a4)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a3, std::nullopt)) ==
hash(Book(a1, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a4, std::nullopt)) ==
hash(Book(a1, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a2, a3, std::nullopt)) ==
hash(Book(a2, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a2, a4, std::nullopt)) ==
hash(Book(a2, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a3, a4, std::nullopt)) ==
hash(Book(a3, a4, std::nullopt)));
BEAST_EXPECT(hash(Book(a1, a2)) != hash(Book(a1, a3)));
BEAST_EXPECT(hash(Book(a1, a2)) != hash(Book(a1, a4)));
BEAST_EXPECT(hash(Book(a1, a2)) != hash(Book(a2, a3)));
BEAST_EXPECT(hash(Book(a1, a2)) != hash(Book(a2, a4)));
BEAST_EXPECT(hash(Book(a1, a2)) != hash(Book(a3, a4)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a3, a4, std::nullopt)));
// Books with domain
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) == hash(Book(a1, a2, domain1)));
BEAST_EXPECT(
hash(Book(a1, a3, domain1)) == hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a4, domain1)) == hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) == hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) == hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) == hash(Book(a3, a4, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
// Comparing Books with domain1 vs no domain
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) != hash(Book(a1, a2, domain1)));
BEAST_EXPECT(
hash(Book(a1, a3, std::nullopt)) != hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a4, std::nullopt)) != hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, std::nullopt)) != hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, std::nullopt)) != hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, std::nullopt)) != hash(Book(a3, a4, domain1)));
// Books with domain1 but different Issues
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) != hash(Book(a3, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) != hash(Book(a1, a4, domain1)));
// Books with domain1 and domain2
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a2, domain2)));
BEAST_EXPECT(
hash(Book(a1, a3, domain1)) != hash(Book(a1, a3, domain2)));
BEAST_EXPECT(
hash(Book(a1, a4, domain1)) != hash(Book(a1, a4, domain2)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a3, domain2)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) != hash(Book(a2, a4, domain2)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) != hash(Book(a3, a4, domain2)));
}
//--------------------------------------------------------------------------
@@ -362,8 +635,16 @@ public:
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2);
Book const b2(a2, a1);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
{
Set c;
@@ -375,11 +656,11 @@ public:
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1)) == 0))
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2)) == 1))
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1)) == 1))
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
@@ -395,11 +676,11 @@ public:
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1)) == 0))
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2)) == 1))
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1)) == 1))
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
@@ -413,6 +694,66 @@ public:
return;
#endif
}
{
Set c;
c.insert(b1_d1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(b2_d1);
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(b1_d2);
if (!BEAST_EXPECT(c.size() == 3))
return;
c.insert(b2_d2);
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Set c;
c.insert(b1);
c.insert(b2);
c.insert(b1_d1);
c.insert(b2_d1);
if (!BEAST_EXPECT(c.size() == 4))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
}
template <class Map>
@@ -425,8 +766,16 @@ public:
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2);
Book const b2(a2, a1);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
// typename Map::value_type value_type;
// std::pair <Book const, int> value_type;
@@ -443,11 +792,11 @@ public:
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1)) == 0))
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2)) == 1))
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1)) == 1))
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
@@ -465,11 +814,77 @@ public:
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1)) == 0))
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2)) == 1))
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1)) == 1))
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
c.insert(std::make_pair(b1_d1, 10));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(b2_d1, 20));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(b1_d2, 30));
if (!BEAST_EXPECT(c.size() == 3))
return;
c.insert(std::make_pair(b2_d2, 40));
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
c.insert(std::make_pair(b1, 1));
c.insert(std::make_pair(b2, 2));
c.insert(std::make_pair(b1_d1, 3));
c.insert(std::make_pair(b2_d1, 4));
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a1, a1, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
@@ -556,6 +971,10 @@ public:
testBookSets();
testBookMaps();
// ---
testIssueDomainSets();
testIssueDomainMaps();
}
};