start of featureAttestations

This commit is contained in:
Richard Holland
2023-12-03 10:50:24 +00:00
parent 27f43ba9ee
commit 54211fb63b
15 changed files with 272 additions and 4 deletions

View File

@@ -455,6 +455,7 @@ target_sources (rippled PRIVATE
src/ripple/app/tx/impl/GenesisMint.cpp
src/ripple/app/tx/impl/Import.cpp
src/ripple/app/tx/impl/Invoke.cpp
src/ripple/app/tx/impl/Attestation.cpp
src/ripple/app/tx/impl/SetSignerList.cpp
src/ripple/app/tx/impl/SetTrust.cpp
src/ripple/app/tx/impl/SignerEntries.cpp

View File

@@ -0,0 +1,150 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
#include <ripple/app/tx/impl/Attest.h>
#include <ripple/basics/Log.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Indexes.h>
namespace ripple {
TxConsequences
Attest::makeTxConsequences(PreflightContext const& ctx)
{
return TxConsequences{ctx.tx, TxConsequences::normal};
}
NotTEC
Attest::preflight(PreflightContext const& ctx)
{
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
auto const flags = ctx.tx.getFlags();
if (ctx.rules.enabled(fix1543) && (flags & tfAttestMask))
return temINVALID_FLAG;
return preflight2(ctx);
}
TER
Attest::preclaim(PreclaimContext const& ctx)
{
if (!ctx.view.rules().enabled(featureAttestations))
return temDISABLED;
auto const id = ctx.tx[sfAccount];
auto const sle = ctx.view.read(keylet::account(id));
if (!sle)
return terNO_ACCOUNT;
Keylet kl =
keylet::attestation(id, ctx.tx.getFieldH256(sfAttestedTxnId));
uint32_t flags = ctx.tx.getFlags();
bool const isDelete = (flags | tfDeleteAttestation) == tfDeleteAttestation;
bool const exists = ctx.view.exists(kl);
if (exists && !isDelete)
return tecDUPLICATE;
else if (!exists && isDelete)
return tecNO_ENTRY;
return tesSUCCESS;
}
TER
Attest::doApply()
{
auto j = ctx_.app.journal("View");
auto const sle = view().peek(keylet::account(account_));
if (!sle)
return tefINTERNAL;
Keylet kl =
keylet::attestation(account_, ctx.tx.getFieldH256(sfAttestedTxnId));
uint32_t flags = ctx_.tx.getFlags();
bool const isDelete = (flags | tfDeleteAttestation) == tfDeleteAttestation;
bool const exists = view().exists(kl);
// delete mode
if (exists && isDelete)
{
auto sleA = view().peek(kl)
AccountID owner = sleA->getAccountID(sfOwner);
auto const page = (*sleA)[sfOwnerNode];
if (!view().dirRemove(
keylet::ownerDir(owner), page, kl.key, true))
{
JLOG(j.fatal())
<< "Could not remove Attestation from owner directory";
return tefBAD_LEDGER;
}
view().erase(sleA);
adjustOwnerCount(view(), sle, -1, j);
return tesSUCCESS;
}
// create mode
else if (!exists && !isDelete)
{
auto sleA = std::make_shared<SLE>(kl);
sleA->setAccountID(sfOwner, account_);
sleA->setFieldH256(sfAttestedTxnID, ctx_.tx.getFieldH256(sfAttestedTxnID));
auto const page = view().dirInsert(
keylet::ownerDir(account_), kl, describeOwnerDir(account_));
JLOG(j_.trace())
<< "Adding Attestation to owner directory " << to_string(kl.key)
<< ": " << (page ? "success" : "failure");
if (!page)
return tecDIR_FULL;
sleA->setFieldU64(sfOwnerNode, *page);
view().insert(sleA);
adjustOwnerCount(view(), sle, 1, j);
return tesSUCCESS;
}
// everything else is invalid
else
return tecINTRERNAL;
}
XRPAmount
Attest::calculateBaseFee(ReadView const& view, STTx const& tx)
{
return Transactor::calculateBaseFee(view, tx);
}
} // namespace ripple

View File

@@ -0,0 +1,57 @@
//------------------------------------------------------------------------------
/*
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_ATTEST_H_INCLUDED
#define RIPPLE_TX_ATTEST_H_INCLUDED
#include <ripple/app/tx/impl/Transactor.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/protocol/Indexes.h>
namespace ripple {
class Attest : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
explicit Attest(ApplyContext& ctx) : Transactor(ctx)
{
}
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace ripple
#endif

View File

@@ -32,6 +32,7 @@
#include <ripple/app/tx/impl/GenesisMint.h>
#include <ripple/app/tx/impl/Import.h>
#include <ripple/app/tx/impl/Invoke.h>
#include <ripple/app/tx/impl/Attest.h>
#include <ripple/app/tx/impl/NFTokenAcceptOffer.h>
#include <ripple/app/tx/impl/NFTokenBurn.h>
#include <ripple/app/tx/impl/NFTokenCancelOffer.h>
@@ -164,6 +165,8 @@ invoke_preflight(PreflightContext const& ctx)
return invoke_preflight_helper<Import>(ctx);
case ttINVOKE:
return invoke_preflight_helper<Invoke>(ctx);
case ttATTEST:
return invoke_preflight_helper<Attest>(ctx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -283,6 +286,8 @@ invoke_preclaim(PreclaimContext const& ctx)
return invoke_preclaim<Import>(ctx);
case ttINVOKE:
return invoke_preclaim<Invoke>(ctx);
case ttATTEST:
return invoke_preclaim<Attest>(ctx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -364,6 +369,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
return Import::calculateBaseFee(view, tx);
case ttINVOKE:
return Invoke::calculateBaseFee(view, tx);
case ttATTEST:
return Attest::calculateBaseFee(view, tx);
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:
@@ -544,6 +551,10 @@ invoke_apply(ApplyContext& ctx)
Invoke p(ctx);
return p();
}
case ttATTEST: {
Attest p(ctx);
return p();
}
case ttURITOKEN_MINT:
case ttURITOKEN_BURN:
case ttURITOKEN_BUY:

View File

@@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 66;
static constexpr std::size_t numFeatures = 67;
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -354,7 +354,7 @@ extern uint256 const featureImport;
extern uint256 const featureXahauGenesis;
extern uint256 const featureHooksUpdate1;
extern uint256 const fixURITokenV1;
extern uint256 const featureAttestations;
} // namespace ripple
#endif

View File

@@ -295,7 +295,10 @@ Keylet
import_vlseq(PublicKey const& key) noexcept;
Keylet
uritoken(AccountID const& issuer, Blob const& uri);
uritoken(AccountID const& issuer, Blob const& uri) noexcept;
Keylet
attestation(AccountID const& issuer, uint256 const& txnid) noexcept;
} // namespace keylet

View File

@@ -52,6 +52,12 @@ namespace ripple {
// clang-format off
enum LedgerEntryType : std::uint16_t
{
/** A ledger object which describes an attested txn
\sa kelet::attestation
*/
ltATTESTATION = 0x0041,
/** A ledger object which describes an account.
\sa keylet::account

View File

@@ -483,6 +483,7 @@ extern SF_UINT256 const sfURITokenID;
extern SF_UINT256 const sfGovernanceFlags;
extern SF_UINT256 const sfGovernanceMarks;
extern SF_UINT256 const sfEmittedTxnID;
extern SF_UINT256 const sfAttestedTxnID;
// currency amount (common)
extern SF_AMOUNT const sfAmount;

View File

@@ -132,6 +132,10 @@ constexpr std::uint32_t const tfStrongTSH = 0x00008000;
constexpr std::uint32_t const tfNFTokenMintOldMask =
~(tfUniversal | tfBurnable | tfOnlyXRP | tfTrustLine | tfTransferable | tfStrongTSH);
// Attest flags:
constexpr std::uint32_t tfDeleteAttestation = 0x00000001;
constexpr std::uint32_t tfAttestMask = ~tfDeleteAttestation;
// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between
// accounts allowed a TrustLine to be added to the issuer of that token
// without explicit permission from that issuer. This was enabled by

View File

@@ -146,6 +146,10 @@ enum TxType : std::uint16_t
ttURITOKEN_CREATE_SELL_OFFER = 48,
ttURITOKEN_CANCEL_SELL_OFFER = 49,
/** This transaction acts as an attestation for another txn currently in the TxQ */
ttATTEST = 94,
/** This transaction can only be used by the genesis account, which is controlled exclusively by
* rewards/governance hooks, to print new XRP to be delivered directly to an array of destinations,
* according to reward schedule */

View File

@@ -460,7 +460,7 @@ REGISTER_FEATURE(Import, Supported::yes, VoteBehavior::De
REGISTER_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(HooksUpdate1, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixURITokenV1, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(Attestations, Supported::yes, VoteBehavior::DefaultNo);
// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.

View File

@@ -45,6 +45,7 @@ namespace ripple {
*/
enum class LedgerNameSpace : std::uint16_t {
ACCOUNT = 'a',
ATTESTATION = 'A',
DIR_NODE = 'd',
TRUST_LINE = 'r',
OFFER = 'o',
@@ -443,6 +444,16 @@ uritoken(AccountID const& issuer, Blob const& uri)
LedgerNameSpace::URI_TOKEN, issuer, Slice{uri.data(), uri.size()})};
}
Keylet attestationDir(uint32_t ledgerSeq)
{
return {ltDIR_NODE, indexHash(LedgerNameSpace::ATTESTATION_DIR, ledgerSeq)};
}
Keylet attestation(AccountID const& issuer, uint256 const& txnid)
{
return {ltATTESTATION, indexHash(LedgerNameSpace::ATTESTATION, issuer, txnid)};
}
} // namespace keylet
} // namespace ripple

View File

@@ -363,6 +363,17 @@ LedgerFormats::LedgerFormats()
},
commonFields);
add(jss::Attestation,
ltATTESTATION,
{
{sfOwner, soeREQUIRED},
{sfOwnerNode, soeREQUIRED},
{sfAttestedTxnID, soeREQUIRED},
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
},
commonField);
// clang-format on
}

View File

@@ -236,6 +236,7 @@ CONSTRUCT_TYPED_SFIELD(sfURITokenID, "URITokenID", UINT256,
CONSTRUCT_TYPED_SFIELD(sfGovernanceFlags, "GovernanceFlags", UINT256, 99);
CONSTRUCT_TYPED_SFIELD(sfGovernanceMarks, "GovernanceMarks", UINT256, 98);
CONSTRUCT_TYPED_SFIELD(sfEmittedTxnID, "EmittedTxnID", UINT256, 97);
CONSTRUCT_TYPED_SFIELD(sfAttestedTxnID, "AttestedTxnID", UINT256, 96);
// currency amount (common)
CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1);

View File

@@ -441,6 +441,14 @@ TxFormats::TxFormats()
{sfTicketSequence, soeOPTIONAL},
},
commonFields);
add(jss::Attest,
ttATTEST,
{
{sfAttestedTxnID, soeREQUIRED},
{sfTicketSequence, soeOPTIONAL},
},
commonFields);
}
TxFormats const&