attestation consensus stuff. compiling not tested

This commit is contained in:
Richard Holland
2023-12-03 15:38:05 +00:00
parent 72016c3e0e
commit d0baeab32f
8 changed files with 124 additions and 0 deletions

View File

@@ -71,6 +71,7 @@ Attest::preclaim(PreclaimContext const& ctx)
else if (!exists && isDelete)
return tecNO_ENTRY;
return tesSUCCESS;
}
@@ -83,6 +84,19 @@ Attest::doApply()
if (!sle)
return tefINTERNAL;
// check for sufficient reserves
{
STAmount const reserve{
view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)};
STAmount const afterFee =
mPriorBalance - ctx_.tx.getFieldAmount(sfFee).xrp();
if (afterFee > mPriorBalance || afterFee < reserve)
return tecINSUFFICIENT_RESERVE;
}
Keylet kl =
keylet::attestation(account_, ctx_.tx.getFieldH256(sfAttestedTxnID));

View File

@@ -85,6 +85,41 @@ preflight0(PreflightContext const& ctx)
return temINVALID;
}
// don't allow attestations unless enabled
if (ctx.tx.isFieldPresent(sfAttesters))
{
if (!ctx.rules.enabled(featureAttestations))
return temDISABLED;
// make sure they can't spam millions of attesters
auto const& attesters = ctx.tx.getFieldArray(sfAttesters);
if (attesters.empty() || attesters.size() > 32)
{
JLOG(ctx.j.warn())
<< "applyTransaction: attesters array too big (max 32) or empty.";
return temMALFORMED;
}
// sanity check entries
std::set<AccountID> used;
for (auto const& attester: attesters)
{
if (attester.getFName() != sfAttesterEntry)
{
JLOG(ctx.j.warn())
<< "applyTransaction: attesters array contained non AttesterEntry object.";
return temMALFORMED;
}
if (used.find(attester.getAccountID(sfAccount)) != used.end())
{
JLOG(ctx.j.warn())
<< "applyTransaction: attesters array contained duplicate attester ID.";
return temMALFORMED;
}
}
}
return tesSUCCESS;
}
@@ -1928,6 +1963,54 @@ Transactor::operator()()
}
}
if (applied && ctx_.tx.isFieldPresent(sfAttesters) && view().rules().enabled(featureAttestations))
{
// delete used attestation objects
auto const& attesters = ctx_.tx.getFieldArray(sfAttesters);
auto const txid = ctx_.tx.getTransactionID();
auto const& j = ctx_.app.journal("View");
for (auto const& attester : attesters)
{
Keylet kl = keylet::attestation(attester.getAccountID(sfAccount), txid);
if (!view().exists(kl))
{
JLOG(j.warn())
<< "Transactor: Warning!!! Attestation does not exist at end of attested txn "
<< txid;
continue;
}
// remove from dir
auto sleA = view().peek(kl);
AccountID owner = sleA->getAccountID(sfOwner);
auto sle = view().peek(keylet::account(owner));
if (!sle)
{
JLOG(j.warn())
<< "Transactor: Warning!!! Attester account does not exist at the end of attested txn "
<< txid;
continue;
}
auto const page = (*sleA)[sfOwnerNode];
if (!view().dirRemove(
keylet::ownerDir(owner), page, kl.key, true))
{
JLOG(j.warn())
<< "Could not remove Attestation from owner directory";
continue;
}
view().erase(sleA);
adjustOwnerCount(view(), sle, -1, j);
}
}
// Post-application (Weak TSH/AAW) Hooks are executed here.
// These TSH do not have the ability to rollback.
// The callback, if any, is also executed here.

View File

@@ -160,6 +160,20 @@ public:
{
// Most transactors do nothing
// after checkSeq/Fee/Sign.
if (ctx.tx.isFieldPresent(sfAttesters) && ctx.view.rules().enabled(featureAttestations))
{
// check if the required attestations are present on ledger
auto const& attesters = ctx.tx.getFieldArray(sfAttesters);
auto const txid = ctx.tx.getTransactionID();
// each required attestation must exist on the ledger to allow the txn through
// otherwise it gets marked retry
for (auto const& attester : attesters)
if (!ctx.view.exists(keylet::attestation(attester.getAccountID(sfAccount), txid)))
return terRETRY;
}
return tesSUCCESS;
}
/////////////////////////////////////////////////////

View File

@@ -591,6 +591,7 @@ extern SField const sfHookGrant;
extern SField const sfActiveValidator;
extern SField const sfImportVLKey;
extern SField const sfHookEmission;
extern SField const sfAttesterEntry;
// array of objects (common)
// ARRAY/1 is reserved for end of array
@@ -618,6 +619,7 @@ extern SField const sfGenesisMints;
extern SField const sfActiveValidators;
extern SField const sfImportVLKeys;
extern SField const sfHookEmissions;
extern SField const sfAttesters;
//------------------------------------------------------------------------------

View File

@@ -141,6 +141,13 @@ InnerObjectFormats::InnerObjectFormats()
{sfPublicKey, soeREQUIRED},
{sfAccount, soeOPTIONAL},
});
add(sfAttesterEntry.jsonName.c_str(),
sfAttesterEntry.getCode(),
{
{sfAccount, soeREQUIRED},
{sfFlags, soeREQUIRED},
});
}
InnerObjectFormats const&

View File

@@ -347,6 +347,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfGenesisMint, "GenesisMint", OBJECT,
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidator, "ActiveValidator", OBJECT, 95);
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKey, "ImportVLKey", OBJECT, 94);
CONSTRUCT_UNTYPED_SFIELD(sfHookEmission, "HookEmission", OBJECT, 93);
CONSTRUCT_UNTYPED_SFIELD(sfAttesterEntry, "AttesterEntry", OBJECT, 90);
// array of objects
// ARRAY/1 is reserved for end of array
@@ -371,6 +372,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfGenesisMints, "GenesisMints", ARRAY,
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidators, "ActiveValidators", ARRAY, 95);
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKeys, "ImportVLKeys", ARRAY, 94);
CONSTRUCT_UNTYPED_SFIELD(sfHookEmissions, "HookEmissions", ARRAY, 93);
CONSTRUCT_UNTYPED_SFIELD(sfAttesters, "Attesters", ARRAY, 91);
// clang-format on

View File

@@ -44,6 +44,7 @@ TxFormats::TxFormats()
{sfNetworkID, soeOPTIONAL},
{sfHookParameters, soeOPTIONAL},
{sfOperationLimit, soeOPTIONAL},
{sfAttesters, soeOPTIONAL},
};
add(jss::AccountSet,

View File

@@ -51,6 +51,7 @@ JSS(Amendments); // ledger type.
JSS(Amount); // in: TransactionSign; field.
JSS(Authorize); // field
JSS(Attest);
JSS(AttesterEntry);
JSS(Attestation);
JSS(Blob);
JSS(Check); // ledger type.