Implement DID (#967)

Fix #918
This commit is contained in:
cyan317
2023-11-03 09:40:40 +00:00
committed by GitHub
parent 25d9e3cc36
commit fa660ef400
12 changed files with 95 additions and 29 deletions

View File

@@ -66,7 +66,7 @@
// Max number of requests to queue up before rejecting further requests. // Max number of requests to queue up before rejecting further requests.
// Defaults to 0, which disables the limit. // Defaults to 0, which disables the limit.
"max_queue_size": 500, "max_queue_size": 500,
// If request contains header with authorization, Clio will check if it matches this value's hash // If request contains header with authorization, Clio will check if it matches this value's sha256 hash
// If matches, the request will be considered as admin request // If matches, the request will be considered as admin request
"admin_password": "xrp", "admin_password": "xrp",
// If local_admin is true, Clio will consider requests come from 127.0.0.1 as admin requests // If local_admin is true, Clio will consider requests come from 127.0.0.1 as admin requests

View File

@@ -22,19 +22,28 @@
namespace rpc { namespace rpc {
// found here : https://xrpl.org/ledger_entry.html#:~:text=valid%20fields%20are%3A-,index,-account_root // found here : https://xrpl.org/ledger_entry.html#:~:text=valid%20fields%20are%3A-,index,-account_root
std::unordered_map<std::string, ripple::LedgerEntryType> const AccountObjectsHandler::TYPESMAP{ std::unordered_map<std::string, ripple::LedgerEntryType> const AccountObjectsHandler::TYPES_MAP{
{"state", ripple::ltRIPPLE_STATE}, {JS(state), ripple::ltRIPPLE_STATE},
{"ticket", ripple::ltTICKET}, {JS(ticket), ripple::ltTICKET},
{"signer_list", ripple::ltSIGNER_LIST}, {JS(signer_list), ripple::ltSIGNER_LIST},
{"payment_channel", ripple::ltPAYCHAN}, {JS(payment_channel), ripple::ltPAYCHAN},
{"offer", ripple::ltOFFER}, {JS(offer), ripple::ltOFFER},
{"escrow", ripple::ltESCROW}, {JS(escrow), ripple::ltESCROW},
{"deposit_preauth", ripple::ltDEPOSIT_PREAUTH}, {JS(deposit_preauth), ripple::ltDEPOSIT_PREAUTH},
{"check", ripple::ltCHECK}, {JS(check), ripple::ltCHECK},
{"nft_page", ripple::ltNFTOKEN_PAGE}, {JS(nft_page), ripple::ltNFTOKEN_PAGE},
{"nft_offer", ripple::ltNFTOKEN_OFFER}, {JS(nft_offer), ripple::ltNFTOKEN_OFFER},
{JS(did), ripple::ltDID},
}; };
std::unordered_set<std::string> const AccountObjectsHandler::TYPES_KEYS = [] {
std::unordered_set<std::string> keys;
std::transform(TYPES_MAP.begin(), TYPES_MAP.end(), std::inserter(keys, keys.begin()), [](auto const& pair) {
return pair.first;
});
return keys;
}();
AccountObjectsHandler::Result AccountObjectsHandler::Result
AccountObjectsHandler::process(AccountObjectsHandler::Input input, Context const& ctx) const AccountObjectsHandler::process(AccountObjectsHandler::Input input, Context const& ctx) const
{ {
@@ -153,7 +162,7 @@ tag_invoke(boost::json::value_to_tag<AccountObjectsHandler::Input>, boost::json:
} }
if (jsonObject.contains(JS(type))) if (jsonObject.contains(JS(type)))
input.type = AccountObjectsHandler::TYPESMAP.at(jv.at(JS(type)).as_string().c_str()); input.type = AccountObjectsHandler::TYPES_MAP.at(jv.at(JS(type)).as_string().c_str());
if (jsonObject.contains(JS(limit))) if (jsonObject.contains(JS(limit)))
input.limit = jv.at(JS(limit)).as_int64(); input.limit = jv.at(JS(limit)).as_int64();

View File

@@ -33,7 +33,7 @@ namespace rpc {
* @brief The account_objects command returns the raw ledger format for all objects owned by an account. * @brief The account_objects command returns the raw ledger format for all objects owned by an account.
* The results can be filtered by the type. * The results can be filtered by the type.
* The valid types are: check, deposit_preauth, escrow, nft_offer, offer, payment_channel, signer_list, state (trust * The valid types are: check, deposit_preauth, escrow, nft_offer, offer, payment_channel, signer_list, state (trust
* line), and ticket. * line), did and ticket.
* *
* For more details see: https://xrpl.org/account_objects.html * For more details see: https://xrpl.org/account_objects.html
*/ */
@@ -42,7 +42,8 @@ class AccountObjectsHandler {
std::shared_ptr<BackendInterface> sharedPtrBackend_; std::shared_ptr<BackendInterface> sharedPtrBackend_;
// constants // constants
static std::unordered_map<std::string, ripple::LedgerEntryType> const TYPESMAP; static std::unordered_map<std::string, ripple::LedgerEntryType> const TYPES_MAP;
static std::unordered_set<std::string> const TYPES_KEYS;
public: public:
static auto constexpr LIMIT_MIN = 10; static auto constexpr LIMIT_MIN = 10;
@@ -89,18 +90,7 @@ public:
modifiers::Clamp<int32_t>(LIMIT_MIN, LIMIT_MAX)}, modifiers::Clamp<int32_t>(LIMIT_MIN, LIMIT_MAX)},
{JS(type), {JS(type),
validation::Type<std::string>{}, validation::Type<std::string>{},
validation::OneOf<std::string>{ validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend())},
"state",
"ticket",
"signer_list",
"payment_channel",
"offer",
"escrow",
"deposit_preauth",
"check",
"nft_page",
"nft_offer",
}},
{JS(marker), validation::AccountMarkerValidator}, {JS(marker), validation::AccountMarkerValidator},
{JS(deletion_blockers_only), validation::Type<bool>{}}, {JS(deletion_blockers_only), validation::Type<bool>{}},
}; };

View File

@@ -56,6 +56,8 @@ std::unordered_map<std::string, ripple::TxType> const AccountTxHandler::TYPESMAP
{JSL(SignerListSet), ripple::ttSIGNER_LIST_SET}, {JSL(SignerListSet), ripple::ttSIGNER_LIST_SET},
{JSL(TicketCreate), ripple::ttTICKET_CREATE}, {JSL(TicketCreate), ripple::ttTICKET_CREATE},
{JSL(TrustSet), ripple::ttTRUST_SET}, {JSL(TrustSet), ripple::ttTRUST_SET},
{JSL(DIDSet), ripple::ttDID_SET},
{JSL(DIDDelete), ripple::ttDID_DELETE},
}; };
// TODO: should be std::views::keys when clang supports it // TODO: should be std::views::keys when clang supports it

View File

@@ -27,6 +27,7 @@ namespace rpc {
std::unordered_map<std::string, ripple::LedgerEntryType> const LedgerDataHandler::TYPES_MAP{ std::unordered_map<std::string, ripple::LedgerEntryType> const LedgerDataHandler::TYPES_MAP{
{JS(account), ripple::ltACCOUNT_ROOT}, {JS(account), ripple::ltACCOUNT_ROOT},
{JS(did), ripple::ltDID},
{JS(amendments), ripple::ltAMENDMENTS}, {JS(amendments), ripple::ltAMENDMENTS},
{JS(check), ripple::ltCHECK}, {JS(check), ripple::ltCHECK},
{JS(deposit_preauth), ripple::ltDEPOSIT_PREAUTH}, {JS(deposit_preauth), ripple::ltDEPOSIT_PREAUTH},

View File

@@ -41,9 +41,9 @@ class LedgerDataHandler {
std::shared_ptr<BackendInterface> sharedPtrBackend_; std::shared_ptr<BackendInterface> sharedPtrBackend_;
util::Logger log_{"RPC"}; util::Logger log_{"RPC"};
static const std::unordered_map<std::string, ripple::LedgerEntryType> TYPES_MAP; static std::unordered_map<std::string, ripple::LedgerEntryType> const TYPES_MAP;
static const std::unordered_set<std::string> TYPES_KEYS; static std::unordered_set<std::string> const TYPES_KEYS;
public: public:
// constants // constants

View File

@@ -32,6 +32,8 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
key = ripple::uint256{std::string_view(*(input.index))}; key = ripple::uint256{std::string_view(*(input.index))};
} else if (input.accountRoot) { } else if (input.accountRoot) {
key = ripple::keylet::account(*ripple::parseBase58<ripple::AccountID>(*(input.accountRoot))).key; key = ripple::keylet::account(*ripple::parseBase58<ripple::AccountID>(*(input.accountRoot))).key;
} else if (input.did) {
key = ripple::keylet::did(*ripple::parseBase58<ripple::AccountID>(*(input.did))).key;
} else if (input.directory) { } else if (input.directory) {
auto const keyOrStatus = composeKeyFromDirectory(*input.directory); auto const keyOrStatus = composeKeyFromDirectory(*input.directory);
if (auto const status = std::get_if<Status>(&keyOrStatus)) if (auto const status = std::get_if<Status>(&keyOrStatus))
@@ -209,6 +211,8 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
// check if request for account root // check if request for account root
else if (jsonObject.contains(JS(account_root))) { else if (jsonObject.contains(JS(account_root))) {
input.accountRoot = jv.at(JS(account_root)).as_string().c_str(); input.accountRoot = jv.at(JS(account_root)).as_string().c_str();
} else if (jsonObject.contains(JS(did))) {
input.did = jv.at(JS(did)).as_string().c_str();
} }
// no need to check if_object again, validator only allows string or object // no need to check if_object again, validator only allows string or object
else if (jsonObject.contains(JS(directory))) { else if (jsonObject.contains(JS(directory))) {

View File

@@ -56,6 +56,8 @@ public:
ripple::LedgerEntryType expectedType = ripple::ltANY; ripple::LedgerEntryType expectedType = ripple::ltANY;
// account id to address account root object // account id to address account root object
std::optional<std::string> accountRoot; std::optional<std::string> accountRoot;
// account id to address did object
std::optional<std::string> did;
// TODO: extract into custom objects, remove json from Input // TODO: extract into custom objects, remove json from Input
std::optional<boost::json::object> directory; std::optional<boost::json::object> directory;
std::optional<boost::json::object> offer; std::optional<boost::json::object> offer;
@@ -128,6 +130,7 @@ public:
{JS(ledger_index), validation::LedgerIndexValidator}, {JS(ledger_index), validation::LedgerIndexValidator},
{JS(index), malformedRequestHexStringValidator}, {JS(index), malformedRequestHexStringValidator},
{JS(account_root), validation::AccountBase58Validator}, {JS(account_root), validation::AccountBase58Validator},
{JS(did), validation::AccountBase58Validator},
{JS(check), malformedRequestHexStringValidator}, {JS(check), malformedRequestHexStringValidator},
{JS(deposit_preauth), {JS(deposit_preauth),
validation::Type<std::string, boost::json::object>{}, validation::Type<std::string, boost::json::object>{},

View File

@@ -1486,6 +1486,22 @@ static auto
generateTransactionTypeTestValues() generateTransactionTypeTestValues()
{ {
return std::vector<AccountTxTransactionBundle>{ return std::vector<AccountTxTransactionBundle>{
AccountTxTransactionBundle{
"DIDSet",
R"({
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"ledger_index": "validated",
"tx_type": "DIDSet"
})",
"[]"},
AccountTxTransactionBundle{
"DIDDelete",
R"({
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"ledger_index": "validated",
"tx_type": "DIDDelete"
})",
"[]"},
AccountTxTransactionBundle{ AccountTxTransactionBundle{
"AccountSet", "AccountSet",
R"({ R"({

View File

@@ -82,6 +82,14 @@ generateTestValuesForParametersTest()
"malformedAddress", "malformedAddress",
"Malformed address."}, "Malformed address."},
ParamTestCaseBundle{
"InvalidDidFormat",
R"({
"did": "invalid"
})",
"malformedAddress",
"Malformed address."},
ParamTestCaseBundle{ ParamTestCaseBundle{
"InvalidAccountRootNotString", "InvalidAccountRootNotString",
R"({ R"({
@@ -1041,6 +1049,17 @@ generateTestValuesForNormalPathTest()
), ),
ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key, ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key,
CreateAccountRootObject(ACCOUNT, 0, 1, 1, 1, INDEX1, 1)}, CreateAccountRootObject(ACCOUNT, 0, 1, 1, 1, INDEX1, 1)},
NormalPathTestBundle{
"DID",
fmt::format(
R"({{
"binary": true,
"did": "{}"
}})",
ACCOUNT
),
ripple::keylet::did(GetAccountIDWithString(ACCOUNT)).key,
CreateDidObject(ACCOUNT, "mydocument", "myURI", "mydata")},
NormalPathTestBundle{ NormalPathTestBundle{
"DirectoryViaDirRoot", "DirectoryViaDirRoot",
fmt::format( fmt::format(

View File

@@ -136,6 +136,25 @@ CreatePaymentTransactionMetaObject(
return metaObj; return metaObj;
} }
ripple::STObject
CreateDidObject(std::string_view accountId, std::string_view didDoc, std::string_view uri, std::string_view data)
{
ripple::STObject did(ripple::sfLedgerEntry);
did.setAccountID(ripple::sfAccount, GetAccountIDWithString(accountId));
did.setFieldU16(ripple::sfLedgerEntryType, ripple::ltDID);
did.setFieldU32(ripple::sfFlags, 0);
did.setFieldU64(ripple::sfOwnerNode, 0);
did.setFieldH256(ripple::sfPreviousTxnID, ripple::uint256{});
did.setFieldU32(ripple::sfPreviousTxnLgrSeq, 0);
ripple::Slice const sliceDoc(didDoc.data(), didDoc.size());
did.setFieldVL(ripple::sfDIDDocument, sliceDoc);
ripple::Slice const sliceUri(uri.data(), uri.size());
did.setFieldVL(ripple::sfURI, sliceUri);
ripple::Slice const sliceData(data.data(), data.size());
did.setFieldVL(ripple::sfData, sliceData);
return did;
}
ripple::STObject ripple::STObject
CreateAccountRootObject( CreateAccountRootObject(
std::string_view accountId, std::string_view accountId,

View File

@@ -286,3 +286,6 @@ CreateAMMObject(
std::string_view asset2Currency, std::string_view asset2Currency,
std::string_view asset2Issuer std::string_view asset2Issuer
); );
[[nodiscard]] ripple::STObject
CreateDidObject(std::string_view accountId, std::string_view didDoc, std::string_view uri, std::string_view data);