mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-26 00:20:20 +00:00
ttSHUFFLE injection appears to be working
This commit is contained in:
@@ -461,6 +461,7 @@ target_sources (rippled PRIVATE
|
||||
src/ripple/app/tx/impl/CronSet.cpp
|
||||
src/ripple/app/tx/impl/DeleteAccount.cpp
|
||||
src/ripple/app/tx/impl/DepositPreauth.cpp
|
||||
src/ripple/app/tx/impl/Entropy.cpp
|
||||
src/ripple/app/tx/impl/Escrow.cpp
|
||||
src/ripple/app/tx/impl/GenesisMint.cpp
|
||||
src/ripple/app/tx/impl/Import.cpp
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
#include <ripple/protocol/BuildInfo.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/app/tx/impl/Entropy.h>
|
||||
#include <ripple/app/tx/impl/Change.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
@@ -225,6 +227,8 @@ RCLConsensus::Adaptor::propose(RCLCxPeerPos::Proposal const& proposal)
|
||||
|
||||
prop.set_signature(sig.data(), sig.size());
|
||||
|
||||
injectShuffleTxn(app_, sig);
|
||||
|
||||
auto const suppression = proposalUniqueId(
|
||||
proposal.position(),
|
||||
proposal.prevLedger(),
|
||||
@@ -652,6 +656,12 @@ RCLConsensus::Adaptor::doAccept(
|
||||
tapNONE,
|
||||
"consensus",
|
||||
[&](OpenView& view, beast::Journal j) {
|
||||
if (rules->enabled(featureRNG))
|
||||
{
|
||||
auto tx = makeEntropyTxn(view, app_, j_);
|
||||
if (tx)
|
||||
app_.getOPs().submitTransaction(tx);
|
||||
}
|
||||
return app_.getTxQ().accept(app_, view);
|
||||
});
|
||||
|
||||
|
||||
@@ -104,12 +104,15 @@ applyTransactions(
|
||||
bool certainRetry = true;
|
||||
std::size_t count = 0;
|
||||
|
||||
// apply the ttSHUFFLE txns first in the ledger to
|
||||
// ensure no one can predict the outcome
|
||||
// then apply ttENTROPY transactions
|
||||
if (view.rules().enabled(featureRNG))
|
||||
for (auto tt : {ttSHUFFLE, ttENTROPY})
|
||||
{
|
||||
// apply the ttRNG txns first in the ledger to ensure no one can predict the outcome
|
||||
for (auto it = txns.begin(); it != txns.end();)
|
||||
{
|
||||
if (it->second->getFieldU16(sfTransactionType) != ttRNG)
|
||||
if (tt != it->second->getFieldU16(sfTransactionType))
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
|
||||
@@ -599,6 +599,12 @@ public:
|
||||
return validatorKeys_.publicKey;
|
||||
}
|
||||
|
||||
SecretKey const&
|
||||
getValidationSecretKey() const override
|
||||
{
|
||||
return validatorKeys_.secretKey;
|
||||
}
|
||||
|
||||
NetworkOPs&
|
||||
getOPs() override
|
||||
{
|
||||
|
||||
@@ -240,6 +240,9 @@ public:
|
||||
|
||||
virtual PublicKey const&
|
||||
getValidationPublicKey() const = 0;
|
||||
|
||||
virtual SecretKey const&
|
||||
getValidationSecretKey() const = 0;
|
||||
|
||||
virtual Resource::Manager&
|
||||
getResourceManager() = 0;
|
||||
|
||||
@@ -228,7 +228,7 @@ public:
|
||||
doTransactionSync(
|
||||
std::shared_ptr<Transaction> transaction,
|
||||
bool bUnlimited,
|
||||
FailHard failType);
|
||||
FailHard failType) override;
|
||||
|
||||
/**
|
||||
* For transactions not submitted by a locally connected client, fire and
|
||||
|
||||
@@ -112,6 +112,14 @@ public:
|
||||
bool bLocal,
|
||||
FailHard failType) = 0;
|
||||
|
||||
|
||||
// directly inject transaction, skipping checks
|
||||
virtual void
|
||||
doTransactionSync(
|
||||
std::shared_ptr<Transaction> transaction,
|
||||
bool bUnlimited,
|
||||
FailHard failType) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Owner functions
|
||||
|
||||
@@ -1478,117 +1478,6 @@ TxQ::accept(Application& app, OpenView& view)
|
||||
}
|
||||
}
|
||||
|
||||
// Inject an RNG psuedo if we're on the UNL
|
||||
if (view.rules().enabled(featureRNG))
|
||||
{
|
||||
JLOG(j_.debug())
|
||||
<< "RNG processing: started";
|
||||
|
||||
auto const seq = view.info().seq;
|
||||
|
||||
do
|
||||
{
|
||||
// if we're not a validator we do nothing here
|
||||
if (app.getValidationPublicKey().empty())
|
||||
break;
|
||||
|
||||
if (seq > 256) // only do the UNL check if its not a newly created test network
|
||||
{
|
||||
// and if we're not on the UNLReport we also do nothing
|
||||
auto const unlRep = view.read(keylet::UNLReport());
|
||||
if (!unlRep || !unlRep->isFieldPresent(sfActiveValidators))
|
||||
{
|
||||
JLOG(j_.debug())
|
||||
<< "RNG processing: UNLReport misssing";
|
||||
// nothing to do without a unlreport object
|
||||
break;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
auto const& avs = unlRep->getFieldArray(sfActiveValidators);
|
||||
for (auto const& av : avs)
|
||||
{
|
||||
if (PublicKey(av[sfPublicKey]) == app.getValidationPublicKey())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
JLOG(j_.debug())
|
||||
<< "RNG processing: UNLReport present but we're not on it";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AccountID acc = calcAccountID(app.getValidationPublicKey());
|
||||
|
||||
static auto getRnd = []() -> uint256 {
|
||||
static std::ifstream rng("/dev/urandom", std::ios::binary);
|
||||
uint256 out;
|
||||
if (rng && rng.read(reinterpret_cast<char*>(out.data()), 32))
|
||||
return out;
|
||||
std::random_device rd;
|
||||
for (auto& word : out)
|
||||
word = rd();
|
||||
return out;
|
||||
};
|
||||
|
||||
static std::map<uint32_t /* ledger seq */ , uint256 /* chosen rnd no */> rngMap;
|
||||
|
||||
uint256 nextRnd = getRnd();
|
||||
|
||||
if (rngMap.find(seq) != rngMap.end())
|
||||
break; // should never happen
|
||||
|
||||
rngMap[seq] = nextRnd;
|
||||
|
||||
std::optional<uint256> prevRnd;
|
||||
|
||||
if (rngMap.find(seq - 1) != rngMap.end())
|
||||
prevRnd = rngMap[seq - 1];
|
||||
|
||||
// amortized cleanup, for every ledger attempt to delete two old entries
|
||||
// even in the most desynced ridiculous state this is guaranteed prevent map growth
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
auto it = rngMap.begin();
|
||||
if (it != rngMap.end() && it->first < seq)
|
||||
rngMap.erase(it->first);
|
||||
}
|
||||
|
||||
// create txn
|
||||
STTx rngTx(ttRNG, [&](auto& obj) {
|
||||
obj.setFieldU32(sfLedgerSequence, seq);
|
||||
obj.setAccountID(sfValidator, acc);
|
||||
if (prevRnd.has_value())
|
||||
obj.setFieldH256(sfRandomData, *prevRnd);
|
||||
obj.setFieldH256(sfNextRandomDigest, sha512Half(nextRnd));
|
||||
// RH TODO: should we sign this??
|
||||
});
|
||||
|
||||
// submit to the ledger
|
||||
{
|
||||
uint256 txID = rngTx.getTransactionID();
|
||||
|
||||
JLOG(j_.debug())
|
||||
<< "RNG processing: Submitting pseudo: "
|
||||
<< rngTx.getFullText()
|
||||
<< " txid: " << txID;
|
||||
auto s = std::make_shared<ripple::Serializer>();
|
||||
rngTx.add(*s);
|
||||
app.getHashRouter().setFlags(txID, SF_PRIVATE2);
|
||||
app.getHashRouter().setFlags(txID, SF_EMITTED);
|
||||
view.rawTxInsert(txID, std::move(s), nullptr);
|
||||
ledgerChanged = true;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
}
|
||||
|
||||
// Inject cron transactions, if any
|
||||
if (view.rules().enabled(featureCron))
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace ripple {
|
||||
@@ -96,7 +97,7 @@ Change::preflight(PreflightContext const& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.tx.getTxnType() == ttRNG && !ctx.rules.enabled(featureRNG))
|
||||
if (ctx.tx.getTxnType() == ttSHUFFLE && !ctx.rules.enabled(featureRNG))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Change: FeatureRNG is not enabled.";
|
||||
return temDISABLED;
|
||||
@@ -110,7 +111,7 @@ Change::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// If tapOPEN_LEDGER is resurrected into ApplyFlags,
|
||||
// this block can be moved to preflight.
|
||||
if (ctx.view.open())
|
||||
if (ctx.view.open() && ctx.tx.getTxnType() != ttSHUFFLE)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Change transaction against open ledger";
|
||||
return temINVALID;
|
||||
@@ -160,7 +161,7 @@ Change::preclaim(PreclaimContext const& ctx)
|
||||
case ttAMENDMENT:
|
||||
case ttUNL_MODIFY:
|
||||
case ttEMIT_FAILURE:
|
||||
case ttRNG:
|
||||
case ttSHUFFLE:
|
||||
return tesSUCCESS;
|
||||
case ttUNL_REPORT: {
|
||||
if (!ctx.tx.isFieldPresent(sfImportVLKey) ||
|
||||
@@ -216,8 +217,8 @@ Change::doApply()
|
||||
return applyEmitFailure();
|
||||
case ttUNL_REPORT:
|
||||
return applyUNLReport();
|
||||
case ttRNG:
|
||||
return applyRNG();
|
||||
case ttSHUFFLE:
|
||||
return applyShuffle();
|
||||
default:
|
||||
assert(0);
|
||||
return tefFAILURE;
|
||||
@@ -225,14 +226,15 @@ Change::doApply()
|
||||
}
|
||||
|
||||
TER
|
||||
Change::applyRNG()
|
||||
Change::applyShuffle()
|
||||
{
|
||||
|
||||
auto const seq = view().info().seq;
|
||||
auto const txSeq = ctx_.tx.getFieldU32(sfLedgerSequence);
|
||||
|
||||
if (seq != ctx_.tx.getFieldU32(sfLedgerSequence))
|
||||
if (seq != txSeq)
|
||||
{
|
||||
JLOG(j_.warn()) << "Change: ttRNG, wrong ledger seq=" << seq;
|
||||
JLOG(j_.warn()) << "Change: ttSHUFFLE, wrong ledger seq. lgr=" << seq << " tx=" << txSeq;
|
||||
return tefFAILURE;
|
||||
}
|
||||
|
||||
@@ -249,9 +251,6 @@ Change::applyRNG()
|
||||
|
||||
if (lastSeq < seq)
|
||||
{
|
||||
// update the ledger sequence of the object
|
||||
sle->setFieldU32(sfLedgerSequence, seq);
|
||||
|
||||
// reset entropy count to zero... this will probably be
|
||||
// one after the below executes but its possible the digest
|
||||
// doesn't match and the entropy count isn't incremented
|
||||
@@ -259,82 +258,25 @@ Change::applyRNG()
|
||||
|
||||
// swap the random data out ready for this round of entropy collection
|
||||
sle->setFieldH256(sfLastRandomData, sle->getFieldH256(sfRandomData));
|
||||
sle->setFieldH256(sfRandomData, beast::zero);
|
||||
}
|
||||
|
||||
uint256 nextDigest = ctx_.tx.getFieldH256(sfNextRandomDigest);
|
||||
uint256 currentEntropy = ctx_.tx.getFieldH256(sfRandomData);
|
||||
uint256 currentDigest = sha512Half(currentEntropy);
|
||||
|
||||
AccountID const validator = ctx_.tx.getAccountID(sfValidator);
|
||||
|
||||
// RH TODO: check if they're on the UNLReport and ignore if not
|
||||
|
||||
// iterate the digest array to find the entry if it exists
|
||||
STArray digestEntries = sle->getFieldArray(sfRandomDigests);
|
||||
std::map<AccountID, STObject> entries;
|
||||
|
||||
for (auto& entry : digestEntries)
|
||||
{
|
||||
// we'll automatically clean up really old entries by just omitting them from
|
||||
// the map here
|
||||
if (entry.getFieldU32(sfLedgerSequence) < seq - 5)
|
||||
continue;
|
||||
|
||||
entries.emplace(entry.getAccountID(sfValidator), std::move(entry));
|
||||
}
|
||||
|
||||
if (auto it = entries.find(validator); it != entries.end())
|
||||
{
|
||||
auto& entry = it->second;
|
||||
|
||||
// ensure the precommitted digest matches the provided entropy
|
||||
if (entry.getFieldH256(sfNextRandomDigest) != currentDigest)
|
||||
{
|
||||
if (entry.getFieldU32(sfLedgerSequence) != seq - 1)
|
||||
{
|
||||
// this is a skip-ahead or missed last txn somehow, so ignore, but no warning.
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is a clear violation so warn (and ignore the entropy)
|
||||
JLOG(j_.warn()) << "!!! Validator " << validator << " supplied entropy that "
|
||||
<< "does not match precommitment value !!!";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// contribute the new entropy to the random data field
|
||||
sle->setFieldH256(sfRandomData, sha512Half(validator, sle->getFieldH256(sfRandomData), currentEntropy));
|
||||
|
||||
// increment entropy count
|
||||
sle->setFieldU16(sfEntropyCount, sle->getFieldU16(sfEntropyCount) + 1);
|
||||
}
|
||||
|
||||
// update the digest entry
|
||||
entry.setFieldH256(sfNextRandomDigest, nextDigest);
|
||||
entry.setFieldU32(sfLedgerSequence, seq);
|
||||
|
||||
// update the ledger sequence of the object
|
||||
sle->setFieldU32(sfLedgerSequence, seq);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this validator doesn't have an entry so create one
|
||||
STObject entry{sfRandomDigestEntry};
|
||||
entry.setAccountID(sfValidator, validator);
|
||||
entry.setFieldH256(sfNextRandomDigest, nextDigest);
|
||||
entry.setFieldU32(sfLedgerSequence, seq);
|
||||
entries.emplace(validator, std::move(entry));
|
||||
// increment entropy count
|
||||
sle->setFieldU16(sfEntropyCount, sle->getFieldU16(sfEntropyCount) + 1);
|
||||
}
|
||||
|
||||
// contribute the new entropy to the random data field
|
||||
sle->setFieldH256(sfRandomData,
|
||||
sha512Half(
|
||||
seq,
|
||||
sle->getFieldU16(sfEntropyCount),
|
||||
sle->getFieldH256(sfRandomData),
|
||||
ctx_.tx.getFieldH256(sfRandomData)));
|
||||
|
||||
// update the array
|
||||
STArray newEntries(sfRandomDigests);
|
||||
newEntries.reserve(entries.size());
|
||||
for (auto& [_, entry] : entries)
|
||||
newEntries.push_back(std::move(entry));
|
||||
|
||||
sle->setFieldArray(sfRandomDigests, std::move(newEntries));
|
||||
|
||||
// send it off to the ledger
|
||||
|
||||
if (!created)
|
||||
view().update(sle);
|
||||
else
|
||||
@@ -1327,4 +1269,49 @@ Change::applyUNLModify()
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
injectShuffleTxn(Application& app, Slice const& sig)
|
||||
{
|
||||
// in featureRNG we use trusted proposal signatures as shuffling entropy
|
||||
// so inject a psuedo to do that here
|
||||
auto ol = app.openLedger().current();
|
||||
if (ol && ol->rules().enabled(featureRNG))
|
||||
{
|
||||
uint256 rnd = sha512Half(std::string("shuffler"), sig);
|
||||
// create txn
|
||||
STTx shuffleTx (ttSHUFFLE, [&](auto& obj) {
|
||||
obj.setFieldU32(sfLedgerSequence, ol->info().seq + 1);
|
||||
obj.setFieldH256(sfRandomData, rnd);
|
||||
obj.setAccountID(sfAccount, AccountID());
|
||||
});
|
||||
|
||||
// inject it into the propose set and into the open ledger
|
||||
uint256 txID = shuffleTx.getTransactionID();
|
||||
|
||||
JLOG(app.journal("Transaction").debug())
|
||||
<< "SHUFFLE processing: Submitting pseudo: "
|
||||
<< shuffleTx.getFullText()
|
||||
<< " txid: " << txID;
|
||||
app.getHashRouter().setFlags(txID, SF_PRIVATE2);
|
||||
app.getHashRouter().setFlags(txID, SF_EMITTED);
|
||||
|
||||
{
|
||||
auto s = std::make_shared<ripple::Serializer>();
|
||||
shuffleTx.add(*s);
|
||||
|
||||
std::unique_lock masterLock{app.getMasterMutex(), std::defer_lock};
|
||||
|
||||
std::unique_lock ledgerLock{
|
||||
app.getLedgerMaster().peekMutex(), std::defer_lock};
|
||||
std::lock(masterLock, ledgerLock);
|
||||
|
||||
app.openLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||
view.rawTxInsert(txID, std::move(s), nullptr);
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -78,9 +78,12 @@ private:
|
||||
applyUNLReport();
|
||||
|
||||
TER
|
||||
applyRNG();
|
||||
applyShuffle();
|
||||
};
|
||||
|
||||
void
|
||||
injectShuffleTxn(Application& app, Slice const& sig);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -442,9 +442,24 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
|
||||
// Only check fee is sufficient when the ledger is open.
|
||||
if (ctx.view.open())
|
||||
{
|
||||
auto const feeDue =
|
||||
|
||||
auto feeDue =
|
||||
minimumFee(ctx.app, baseFee, ctx.view.fees(), ctx.flags);
|
||||
|
||||
if (ctx.view.rules().enabled(featureRNG))
|
||||
{
|
||||
auto const pkSignerField = ctx.tx.getSigningPubKey();
|
||||
if (publicKeyType(makeSlice(pkSignerField)))
|
||||
{
|
||||
PublicKey pkSigner {makeSlice(pkSignerField)};
|
||||
if (inUNLReport(ctx.view, ctx.app, pkSigner, ctx.j))
|
||||
{
|
||||
// UVTxns don't have to pay a fee
|
||||
feeDue = beast::zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (feePaid < feeDue)
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
@@ -541,6 +556,16 @@ Transactor::checkSeqProxy(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
if (view.rules().enabled(featureRNG) &&
|
||||
tx.getTxnType() == ttENTROPY && t_seqProx.isSeq() &&
|
||||
tx[sfSequence] == 0)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "applyTransaction: allowing ttENTROPY with seq=0 "
|
||||
<< toBase58(id);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
JLOG(j.trace())
|
||||
<< "applyTransaction: delay: source account does not exist "
|
||||
<< toBase58(id);
|
||||
@@ -848,19 +873,32 @@ NotTEC
|
||||
Transactor::checkSingleSign(PreclaimContext const& ctx)
|
||||
{
|
||||
// Check that the value in the signing key slot is a public key.
|
||||
auto const pkSigner = ctx.tx.getSigningPubKey();
|
||||
if (!publicKeyType(makeSlice(pkSigner)))
|
||||
auto const pkSignerField = ctx.tx.getSigningPubKey();
|
||||
if (!publicKeyType(makeSlice(pkSignerField)))
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "checkSingleSign: signing public key type is unknown";
|
||||
return tefBAD_AUTH; // FIXME: should be better error!
|
||||
}
|
||||
|
||||
PublicKey pkSigner {makeSlice(pkSignerField)};
|
||||
|
||||
// Look up the account.
|
||||
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
|
||||
auto const idSigner = calcAccountID(pkSigner);
|
||||
auto const idAccount = ctx.tx.getAccountID(sfAccount);
|
||||
auto const sleAccount = ctx.view.read(keylet::account(idAccount));
|
||||
|
||||
// check if this is a UVTxn (UNL Validator transaction)
|
||||
// these are signed by the UNL validators
|
||||
if (ctx.view.rules().enabled(featureRNG))
|
||||
{
|
||||
// UVTxns of the approved type don't need an underlying account
|
||||
// and can be signed with the manifest ephemeral key
|
||||
if (inUNLReport(ctx.view, ctx.app, pkSigner, ctx.j) &&
|
||||
ctx.tx.getTxnType() == ttENTROPY)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
if (!sleAccount)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include <ripple/app/tx/impl/SetTrust.h>
|
||||
#include <ripple/app/tx/impl/URIToken.h>
|
||||
#include <ripple/app/tx/impl/XahauGenesis.h>
|
||||
#include <ripple/app/tx/impl/Entropy.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -152,7 +153,7 @@ invoke_preflight(PreflightContext const& ctx)
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttEMIT_FAILURE:
|
||||
case ttRNG:
|
||||
case ttSHUFFLE:
|
||||
return invoke_preflight_helper<Change>(ctx);
|
||||
case ttHOOK_SET:
|
||||
return invoke_preflight_helper<SetHook>(ctx);
|
||||
@@ -188,6 +189,8 @@ invoke_preflight(PreflightContext const& ctx)
|
||||
return invoke_preflight_helper<CronSet>(ctx);
|
||||
case ttCRON:
|
||||
return invoke_preflight_helper<Cron>(ctx);
|
||||
case ttENTROPY:
|
||||
return invoke_preflight_helper<Entropy>(ctx);
|
||||
default:
|
||||
assert(false);
|
||||
return {temUNKNOWN, TxConsequences{temUNKNOWN}};
|
||||
@@ -284,7 +287,7 @@ invoke_preclaim(PreclaimContext const& ctx)
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttEMIT_FAILURE:
|
||||
case ttRNG:
|
||||
case ttSHUFFLE:
|
||||
return invoke_preclaim<Change>(ctx);
|
||||
case ttNFTOKEN_MINT:
|
||||
return invoke_preclaim<NFTokenMint>(ctx);
|
||||
@@ -318,6 +321,8 @@ invoke_preclaim(PreclaimContext const& ctx)
|
||||
return invoke_preclaim<CronSet>(ctx);
|
||||
case ttCRON:
|
||||
return invoke_preclaim<Cron>(ctx);
|
||||
case ttENTROPY:
|
||||
return invoke_preclaim<Entropy>(ctx);
|
||||
default:
|
||||
assert(false);
|
||||
return temUNKNOWN;
|
||||
@@ -376,7 +381,7 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttEMIT_FAILURE:
|
||||
case ttRNG:
|
||||
case ttSHUFFLE:
|
||||
return Change::calculateBaseFee(view, tx);
|
||||
case ttNFTOKEN_MINT:
|
||||
return NFTokenMint::calculateBaseFee(view, tx);
|
||||
@@ -410,6 +415,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
return CronSet::calculateBaseFee(view, tx);
|
||||
case ttCRON:
|
||||
return Cron::calculateBaseFee(view, tx);
|
||||
case ttENTROPY:
|
||||
return Entropy::calculateBaseFee(view, tx);
|
||||
default:
|
||||
return XRPAmount{0};
|
||||
}
|
||||
@@ -547,7 +554,7 @@ invoke_apply(ApplyContext& ctx)
|
||||
case ttFEE:
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttRNG:
|
||||
case ttSHUFFLE:
|
||||
case ttEMIT_FAILURE: {
|
||||
Change p(ctx);
|
||||
return p();
|
||||
@@ -612,6 +619,10 @@ invoke_apply(ApplyContext& ctx)
|
||||
Cron p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttENTROPY: {
|
||||
Entropy p(ctx);
|
||||
return p();
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
return {temUNKNOWN, false};
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/ledger/OpenLedger.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/app/misc/HashRouter.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include <ripple/protocol/STTx.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/misc/Manifest.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -1094,6 +1096,70 @@ trustTransferLockedBalance(
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <class V>
|
||||
bool inUNLReport(
|
||||
V const& view,
|
||||
AccountID const& id,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
|
||||
auto const seq = view.info().seq;
|
||||
static uint32_t lastLgrSeq = 0;
|
||||
static std::map<AccountID, bool> cache;
|
||||
|
||||
// for the first 256 ledgers we're just saying everyone is in the UNLReport
|
||||
// because otherwise testing is very difficult.
|
||||
if (seq < 256)
|
||||
return true;
|
||||
|
||||
if (lastLgrSeq != seq)
|
||||
{
|
||||
cache.clear();
|
||||
lastLgrSeq = seq;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cache.find(id) != cache.end())
|
||||
return cache[id];
|
||||
}
|
||||
|
||||
// Check if UVAcc is on UNLReport we also do nothing
|
||||
auto const unlRep = view.read(keylet::UNLReport());
|
||||
if (!unlRep || !unlRep->isFieldPresent(sfActiveValidators))
|
||||
{
|
||||
JLOG(j.debug())
|
||||
<< "UNLReport misssing";
|
||||
|
||||
// ensure we keep the cache invalid when in this state
|
||||
lastLgrSeq = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const& avs = unlRep->getFieldArray(sfActiveValidators);
|
||||
for (auto const& av : avs)
|
||||
{
|
||||
if (av.getAccountID(sfAccount) == id)
|
||||
return cache[id] = true;
|
||||
}
|
||||
|
||||
return cache[id] = false;
|
||||
}
|
||||
|
||||
|
||||
template <class V>
|
||||
bool inUNLReport(
|
||||
V const& view,
|
||||
Application& app,
|
||||
PublicKey const& pk,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
PublicKey uvPk = app.validatorManifests().getMasterKey(pk);
|
||||
|
||||
return inUNLReport(view, calcAccountID(pk), j) ||
|
||||
(uvPk != pk && inUNLReport(view, calcAccountID(uvPk), j));
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include <ripple/overlay/impl/Tuning.h>
|
||||
#include <ripple/overlay/predicates.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/app/tx/impl/Change.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@@ -1955,6 +1957,9 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMProposeSet> const& m)
|
||||
if (!isTrusted && app_.config().RELAY_UNTRUSTED_PROPOSALS == -1)
|
||||
return;
|
||||
|
||||
// ttSHUFFLE is injected as part of featureRNG, based on the proposal signature
|
||||
injectShuffleTxn(app_, makeSlice(set.signature()));
|
||||
|
||||
uint256 const proposeHash{set.currenttxhash()};
|
||||
uint256 const prevLedger{set.previousledger()};
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ enum LedgerEntryType : std::uint16_t
|
||||
ltEMITTED_TXN = 'E',
|
||||
|
||||
|
||||
/** A ledger object containing a consensus-generated random number, operated on by ttRNG
|
||||
/** A ledger object containing a consensus-generated random number, operated on by ttENTROPY
|
||||
|
||||
\sa keylet::rng
|
||||
*/
|
||||
|
||||
@@ -149,9 +149,13 @@ enum TxType : std::uint16_t
|
||||
ttURITOKEN_CREATE_SELL_OFFER = 48,
|
||||
ttURITOKEN_CANCEL_SELL_OFFER = 49,
|
||||
|
||||
/* A pseudo-txn used by featureRNG which allows validators to submit blinded entropy
|
||||
/* A pseudo-txn type used by featureRNG to shuffle and randomise the transaction set based on proposal
|
||||
* signatures */
|
||||
ttSHUFFLE = 88,
|
||||
|
||||
/* A UNLReport-validator only txn by featureRNG which allows validators to submit blinded entropy
|
||||
* to a consensus based random number system */
|
||||
ttRNG = 89,
|
||||
ttENTROPY = 89,
|
||||
|
||||
/* A pseudo-txn alarm signal for invoking a hook, emitted by validators after alarm set conditions are met */
|
||||
ttCRON = 92,
|
||||
|
||||
@@ -615,8 +615,7 @@ isPseudoTx(STObject const& tx)
|
||||
|
||||
auto tt = safe_cast<TxType>(*t);
|
||||
return tt == ttAMENDMENT || tt == ttFEE || tt == ttUNL_MODIFY ||
|
||||
tt == ttEMIT_FAILURE || tt == ttUNL_REPORT || tt == ttCRON ||
|
||||
tt == ttRNG;
|
||||
tt == ttEMIT_FAILURE || tt == ttUNL_REPORT || tt == ttCRON || tt == ttSHUFFLE;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -491,15 +491,24 @@ TxFormats::TxFormats()
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::Rng,
|
||||
ttRNG,
|
||||
add(jss::Entropy,
|
||||
ttENTROPY,
|
||||
{
|
||||
{sfValidator, soeREQUIRED},
|
||||
{sfRandomData, soeREQUIRED},
|
||||
{sfNextRandomDigest, soeREQUIRED},
|
||||
{sfLedgerSequence, soeREQUIRED},
|
||||
{sfParentHash, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::Shuffle,
|
||||
ttSHUFFLE,
|
||||
{
|
||||
{sfLedgerSequence, soeREQUIRED},
|
||||
{sfRandomData, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
}
|
||||
|
||||
TxFormats const&
|
||||
|
||||
@@ -142,6 +142,8 @@ JSS(HookState); // ledger type.
|
||||
JSS(HookStateData); // field.
|
||||
JSS(HookStateKey); // field.
|
||||
JSS(EmittedTxn); // ledger type.
|
||||
JSS(Entropy);
|
||||
JSS(Shuffle);
|
||||
JSS(SignerList); // ledger type.
|
||||
JSS(SignerListSet); // transaction type.
|
||||
JSS(SigningPubKey); // field.
|
||||
|
||||
Reference in New Issue
Block a user