Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DID: Decentralized identifiers (DIDs) (XLS-40): #4636

Merged
merged 28 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cef1f64
initial DID implementation
mvadari Jul 18, 2023
2f4c285
add RPC tests
mvadari Aug 17, 2023
99b53be
respond to comments, handle max blob length
mvadari Aug 17, 2023
5916f5f
add AccountDelete test
mvadari Aug 17, 2023
7ee38f9
update to latest version
mvadari Sep 15, 2023
27ad50c
merge develop
mvadari Sep 15, 2023
ed9e915
merge develop
mvadari Sep 15, 2023
10b6ff7
Merge branch 'develop' into did
mvadari Sep 18, 2023
7fc2015
Merge branch 'develop' into did
mvadari Sep 19, 2023
298077f
respond to comments
mvadari Sep 26, 2023
04066ae
add another simplification lambda
mvadari Sep 26, 2023
13dac8e
Merge branch 'develop' into did
mvadari Oct 3, 2023
7609a66
rename sfAttestation -> sfData
mvadari Oct 3, 2023
4705908
rename DID -> did in test
mvadari Oct 6, 2023
f347bae
make test case names more descriptive
mvadari Oct 6, 2023
6bda30b
fix minor issues
mvadari Oct 6, 2023
31117f7
move helper functions to View.h
mvadari Oct 6, 2023
4acc54b
add docs
mvadari Oct 6, 2023
b0c99a4
update cmake file
mvadari Oct 6, 2023
fa63fc3
actual cmake fix
mvadari Oct 6, 2023
36716d6
Revert "move helper functions to View.h"
mvadari Oct 6, 2023
c6e6b26
fix formatting
mvadari Oct 6, 2023
d02176b
Merge branch 'develop' into did
mvadari Oct 7, 2023
7eb733f
Merge branch 'develop' into did
mvadari Oct 11, 2023
d65b77f
Merge branch 'develop' into did
intelliot Oct 12, 2023
9885bf5
Merge branch 'develop' into did
mvadari Oct 17, 2023
ea560af
Update to latest spec
mvadari Oct 17, 2023
7c2768d
Merge branch 'develop' into did
intelliot Oct 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Builds/CMake/RippledCore.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ target_sources (rippled PRIVATE
src/ripple/app/tx/impl/CreateTicket.cpp
src/ripple/app/tx/impl/DeleteAccount.cpp
src/ripple/app/tx/impl/DepositPreauth.cpp
src/ripple/app/tx/impl/DID.cpp
src/ripple/app/tx/impl/Escrow.cpp
src/ripple/app/tx/impl/InvariantCheck.cpp
src/ripple/app/tx/impl/NFTokenAcceptOffer.cpp
Expand Down Expand Up @@ -785,6 +786,7 @@ if (tests)
src/test/app/DeliverMin_test.cpp
src/test/app/DepositAuth_test.cpp
src/test/app/Discrepancy_test.cpp
src/test/app/DID_test.cpp
src/test/app/DNS_test.cpp
src/test/app/Escrow_test.cpp
src/test/app/FeeVote_test.cpp
Expand Down Expand Up @@ -937,6 +939,7 @@ if (tests)
src/test/jtx/impl/check.cpp
src/test/jtx/impl/delivermin.cpp
src/test/jtx/impl/deposit.cpp
src/test/jtx/impl/did.cpp
src/test/jtx/impl/envconfig.cpp
src/test/jtx/impl/fee.cpp
src/test/jtx/impl/flags.cpp
Expand Down
238 changes: 238 additions & 0 deletions src/ripple/app/tx/impl/DID.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 Ripple Labs Inc.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================

#include <ripple/app/tx/impl/DID.h>

#include <ripple/basics/Log.h>
#include <ripple/ledger/ApplyView.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/st.h>

namespace ripple {

/*
DID
======

Decentralized Identifiers (DIDs) are a new type of identifier that enable
verifiable, self-sovereign digital identity and are designed to be
compatible with any distributed ledger or network. This implementation
conforms to the requirements specified in the DID v1.0 specification
currently recommended by the W3C Credentials Community Group
(https://www.w3.org/TR/did-core/).
*/

//------------------------------------------------------------------------------

NotTEC
DIDSet::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureDID))
return temDISABLED;

if (ctx.tx.getFlags() & tfUniversalMask)
return temINVALID_FLAG;

if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;

if (!ctx.tx.isFieldPresent(sfURI) &&
!ctx.tx.isFieldPresent(sfDIDDocument) && !ctx.tx.isFieldPresent(sfData))
return temEMPTY_DID;

if (ctx.tx.isFieldPresent(sfURI) && ctx.tx[sfURI].empty() &&
ctx.tx.isFieldPresent(sfDIDDocument) && ctx.tx[sfDIDDocument].empty())
return temEMPTY_DID;

mvadari marked this conversation as resolved.
Show resolved Hide resolved
auto isTooLong = [&](auto const& sField, std::size_t length) -> bool {
if (auto field = ctx.tx[~sField])
return field->length() > length;
return false;
};

if (isTooLong(sfURI, maxDIDURILength) ||
isTooLong(sfDIDDocument, maxDIDDocumentLength) ||
isTooLong(sfData, maxDIDAttestationLength))
return temMALFORMED;

return preflight2(ctx);
}

TER
DIDSet::preclaim(PreclaimContext const& ctx)
{
if (!ctx.view.exists(keylet::did(ctx.tx[sfAccount])) &&
!ctx.tx.isFieldPresent(sfURI) && !ctx.tx.isFieldPresent(sfDIDDocument))
{
// Need either the URI or document if the account doesn't already have a
// DID
return tecEMPTY_DID;
}

return tesSUCCESS;
}

TER
addSLE(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's your call, but I don't find much value in making this a separate function. I'd probably just make a block in the one place it's called. But also fine as-is. (Although it should live in an anonymous namespace if you do keep it a separate function).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it a separate function because I'd like to make it a helper function for the future, but I figured that the refactor wasn't worth doing in this PR and could wait for a separate PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I'm fine with it as-is. Thanks for the explanation.

ApplyContext& ctx,
std::shared_ptr<SLE> const& sle,
AccountID const& owner)
{
auto const sleAccount = ctx.view().peek(keylet::account(owner));
if (!sleAccount)
return tefINTERNAL;

// Check reserve availability for new object creation
{
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
auto const reserve =
ctx.view().fees().accountReserve((*sleAccount)[sfOwnerCount] + 1);

if (balance < reserve)
return tecINSUFFICIENT_RESERVE;
}

// Add ledger object to ledger
ctx.view().insert(sle);

// Add ledger object to owner's page
{
auto page = ctx.view().dirInsert(
keylet::ownerDir(owner), sle->key(), describeOwnerDir(owner));
if (!page)
return tecDIR_FULL;
(*sle)[sfOwnerNode] = *page;
}
adjustOwnerCount(ctx.view(), sleAccount, 1, ctx.journal);
ctx.view().update(sleAccount);

return tesSUCCESS;
}

TER
DIDSet::doApply()
{
// Edit ledger object if it already exists
Keylet const didKeylet = keylet::did(account_);
if (auto const sleDID = ctx_.view().peek(didKeylet))
{
mvadari marked this conversation as resolved.
Show resolved Hide resolved
auto update = [&](auto const& sField) {
if (auto const field = ctx_.tx[~sField])
{
if (field->empty())
{
sleDID->makeFieldAbsent(sField);
}
else
{
(*sleDID)[sField] = *field;
}
}
};
update(sfURI);
update(sfDIDDocument);
update(sfData);

if (!sleDID->isFieldPresent(sfURI) &&
!sleDID->isFieldPresent(sfDIDDocument))
{
return tecEMPTY_DID;
}
ctx_.view().update(sleDID);
return tesSUCCESS;
}

// Create new ledger object otherwise
auto const sleDID = std::make_shared<SLE>(didKeylet);
(*sleDID)[sfAccount] = account_;

auto set = [&](auto const& sField) {
if (auto const field = ctx_.tx[~sField]; field && !field->empty())
(*sleDID)[sField] = *field;
};

set(sfURI);
set(sfDIDDocument);
set(sfData);

return addSLE(ctx_, sleDID, account_);
}

NotTEC
DIDDelete::preflight(PreflightContext const& ctx)
mvadari marked this conversation as resolved.
Show resolved Hide resolved
{
if (!ctx.rules.enabled(featureDID))
return temDISABLED;

if (ctx.tx.getFlags() & tfUniversalMask)
return temINVALID_FLAG;

if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;

return preflight2(ctx);
}

TER
DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner)
seelabs marked this conversation as resolved.
Show resolved Hide resolved
{
auto const sle = ctx.view().peek(sleKeylet);
if (!sle)
return tecNO_ENTRY;

return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal);
}

TER
DIDDelete::deleteSLE(
ApplyView& view,
std::shared_ptr<SLE> sle,
AccountID const owner,
beast::Journal j)
{
// Remove object from owner directory
if (!view.dirRemove(
keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true))
{
JLOG(j.fatal()) << "Unable to delete DID Token from owner.";
return tefBAD_LEDGER;
}

auto const sleOwner = view.peek(keylet::account(owner));
mvadari marked this conversation as resolved.
Show resolved Hide resolved
if (!sleOwner)
return tecINTERNAL;

adjustOwnerCount(view, sleOwner, -1, j);
view.update(sleOwner);

// Remove object from ledger
view.erase(sle);
return tesSUCCESS;
}

TER
DIDDelete::doApply()
{
mvadari marked this conversation as resolved.
Show resolved Hide resolved
return deleteSLE(ctx_, keylet::did(account_), account_);
}

} // namespace ripple
76 changes: 76 additions & 0 deletions src/ripple/app/tx/impl/DID.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================

#ifndef RIPPLE_TX_DID_H_INCLUDED
#define RIPPLE_TX_DID_H_INCLUDED

#include <ripple/app/tx/impl/Transactor.h>

namespace ripple {

class DIDSet : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};

explicit DIDSet(ApplyContext& ctx) : Transactor(ctx)
{
}

static NotTEC
preflight(PreflightContext const& ctx);

static TER
preclaim(PreclaimContext const& ctx);

TER
doApply() override;
};

//------------------------------------------------------------------------------

class DIDDelete : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};

explicit DIDDelete(ApplyContext& ctx) : Transactor(ctx)
{
}

static NotTEC
preflight(PreflightContext const& ctx);

static TER
deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner);

static TER
deleteSLE(
ApplyView& view,
std::shared_ptr<SLE> sle,
AccountID const owner,
beast::Journal j);

TER
doApply() override;
};

} // namespace ripple

#endif
15 changes: 15 additions & 0 deletions src/ripple/app/tx/impl/DeleteAccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
//==============================================================================

#include <ripple/app/tx/impl/DID.h>
#include <ripple/app/tx/impl/DeleteAccount.h>
#include <ripple/app/tx/impl/DepositPreauth.h>
#include <ripple/app/tx/impl/SetSignerList.h>
Expand Down Expand Up @@ -133,6 +134,18 @@ removeNFTokenOfferFromLedger(
return tesSUCCESS;
}

TER
removeDIDFromLedger(
Application& app,
ApplyView& view,
AccountID const& account,
uint256 const& delIndex,
std::shared_ptr<SLE> const& sleDel,
beast::Journal j)
{
return DIDDelete::deleteSLE(view, sleDel, account, j);
}

// Return nullptr if the LedgerEntryType represents an obligation that can't
// be deleted. Otherwise return the pointer to the function that can delete
// the non-obligation
Expand All @@ -151,6 +164,8 @@ nonObligationDeleter(LedgerEntryType t)
return removeDepositPreauthFromLedger;
case ltNFTOKEN_OFFER:
return removeNFTokenOfferFromLedger;
case ltDID:
return removeDIDFromLedger;
default:
return nullptr;
}
Expand Down
1 change: 1 addition & 0 deletions src/ripple/app/tx/impl/InvariantCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ LedgerEntryTypesMatch::visitEntry(
case ltBRIDGE:
case ltXCHAIN_OWNED_CLAIM_ID:
case ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID:
case ltDID:
break;
default:
invalidTypeAdded_ = true;
Expand Down
Loading
Loading