mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Check consensus hash consistency (RIPD-1456):
These changes use the hash of the consensus transaction set when characterizing the mismatch between a locally built ledger and fully validated network ledger. This allows detection of non-determinism in transaction process, in which consensus succeeded, but a node somehow generated a different subsequent ledger.
This commit is contained in:
@@ -4697,6 +4697,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\test\app\LedgerHistory_test.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\test\app\LedgerLoad_test.cpp">
|
<ClCompile Include="..\..\src\test\app\LedgerLoad_test.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -5637,6 +5637,9 @@
|
|||||||
<ClCompile Include="..\..\src\test\app\HashRouter_test.cpp">
|
<ClCompile Include="..\..\src\test\app\HashRouter_test.cpp">
|
||||||
<Filter>test\app</Filter>
|
<Filter>test\app</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\test\app\LedgerHistory_test.cpp">
|
||||||
|
<Filter>test\app</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\test\app\LedgerLoad_test.cpp">
|
<ClCompile Include="..\..\src\test\app\LedgerLoad_test.cpp">
|
||||||
<Filter>test\app</Filter>
|
<Filter>test\app</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -205,17 +205,17 @@ RCLConsensus::Adaptor::propose(RCLCxPeerPos::Proposal const& proposal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::Adaptor::share(RCLTxSet const& set)
|
RCLConsensus::Adaptor::share(RCLTxSet const& txns)
|
||||||
{
|
{
|
||||||
inboundTransactions_.giveSet(set.id(), set.map_, false);
|
inboundTransactions_.giveSet(txns.id(), txns.map_, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<RCLTxSet>
|
boost::optional<RCLTxSet>
|
||||||
RCLConsensus::Adaptor::acquireTxSet(RCLTxSet::ID const& setId)
|
RCLConsensus::Adaptor::acquireTxSet(RCLTxSet::ID const& setId)
|
||||||
{
|
{
|
||||||
if (auto set = inboundTransactions_.getSet(setId, true))
|
if (auto txns = inboundTransactions_.getSet(setId, true))
|
||||||
{
|
{
|
||||||
return RCLTxSet{std::move(set)};
|
return RCLTxSet{std::move(txns)};
|
||||||
}
|
}
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
@@ -422,11 +422,11 @@ RCLConsensus::Adaptor::doAccept(
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Put transactions into a deterministic, but unpredictable, order
|
// Put transactions into a deterministic, but unpredictable, order
|
||||||
CanonicalTXSet retriableTxs{result.set.id()};
|
CanonicalTXSet retriableTxs{result.txns.id()};
|
||||||
|
|
||||||
auto sharedLCL = buildLCL(
|
auto sharedLCL = buildLCL(
|
||||||
prevLedger,
|
prevLedger,
|
||||||
result.set,
|
result.txns,
|
||||||
consensusCloseTime,
|
consensusCloseTime,
|
||||||
closeTimeCorrect,
|
closeTimeCorrect,
|
||||||
closeResolution,
|
closeResolution,
|
||||||
@@ -449,14 +449,15 @@ RCLConsensus::Adaptor::doAccept(
|
|||||||
if (validating_ && !consensusFail &&
|
if (validating_ && !consensusFail &&
|
||||||
app_.getValidations().canValidateSeq(sharedLCL.seq()))
|
app_.getValidations().canValidateSeq(sharedLCL.seq()))
|
||||||
{
|
{
|
||||||
validate(sharedLCL, proposing);
|
validate(sharedLCL, result.txns, proposing);
|
||||||
JLOG(j_.info()) << "CNF Val " << newLCLHash;
|
JLOG(j_.info()) << "CNF Val " << newLCLHash;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
JLOG(j_.info()) << "CNF buildLCL " << newLCLHash;
|
JLOG(j_.info()) << "CNF buildLCL " << newLCLHash;
|
||||||
|
|
||||||
// See if we can accept a ledger as fully-validated
|
// See if we can accept a ledger as fully-validated
|
||||||
ledgerMaster_.consensusBuilt(sharedLCL.ledger_, std::move(consensusJson));
|
ledgerMaster_.consensusBuilt(
|
||||||
|
sharedLCL.ledger_, result.txns.id(), std::move(consensusJson));
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
{
|
{
|
||||||
@@ -636,7 +637,7 @@ RCLConsensus::Adaptor::notify(
|
|||||||
Typically the txFilter is used to reject transactions
|
Typically the txFilter is used to reject transactions
|
||||||
that already accepted in the prior ledger.
|
that already accepted in the prior ledger.
|
||||||
|
|
||||||
@param set set of transactions to apply
|
@param txns set of transactions to apply
|
||||||
@param view ledger to apply to
|
@param view ledger to apply to
|
||||||
@param txFilter callback, return false to reject txn
|
@param txFilter callback, return false to reject txn
|
||||||
@return retriable transactions
|
@return retriable transactions
|
||||||
@@ -645,13 +646,13 @@ RCLConsensus::Adaptor::notify(
|
|||||||
CanonicalTXSet
|
CanonicalTXSet
|
||||||
applyTransactions(
|
applyTransactions(
|
||||||
Application& app,
|
Application& app,
|
||||||
RCLTxSet const& cSet,
|
RCLTxSet const& txns,
|
||||||
OpenView& view,
|
OpenView& view,
|
||||||
std::function<bool(uint256 const&)> txFilter)
|
std::function<bool(uint256 const&)> txFilter)
|
||||||
{
|
{
|
||||||
auto j = app.journal("LedgerConsensus");
|
auto j = app.journal("LedgerConsensus");
|
||||||
|
|
||||||
auto& set = *(cSet.map_);
|
auto& set = *(txns.map_);
|
||||||
CanonicalTXSet retriableTxs(set.getHash().as_uint256());
|
CanonicalTXSet retriableTxs(set.getHash().as_uint256());
|
||||||
|
|
||||||
for (auto const& item : set)
|
for (auto const& item : set)
|
||||||
@@ -731,7 +732,7 @@ applyTransactions(
|
|||||||
RCLCxLedger
|
RCLCxLedger
|
||||||
RCLConsensus::Adaptor::buildLCL(
|
RCLConsensus::Adaptor::buildLCL(
|
||||||
RCLCxLedger const& previousLedger,
|
RCLCxLedger const& previousLedger,
|
||||||
RCLTxSet const& set,
|
RCLTxSet const& txns,
|
||||||
NetClock::time_point closeTime,
|
NetClock::time_point closeTime,
|
||||||
bool closeTimeCorrect,
|
bool closeTimeCorrect,
|
||||||
NetClock::duration closeResolution,
|
NetClock::duration closeResolution,
|
||||||
@@ -746,9 +747,9 @@ RCLConsensus::Adaptor::buildLCL(
|
|||||||
closeTimeCorrect = ((replay->closeFlags_ & sLCF_NoConsensusTime) == 0);
|
closeTimeCorrect = ((replay->closeFlags_ & sLCF_NoConsensusTime) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
JLOG(j_.debug()) << "Report: TxSt = " << set.id() << ", close "
|
JLOG(j_.debug()) << "Report: TxSt = " << txns.id() << ", close "
|
||||||
<< closeTime.time_since_epoch().count()
|
<< closeTime.time_since_epoch().count()
|
||||||
<< (closeTimeCorrect ? "" : "X");
|
<< (closeTimeCorrect ? "" : " (incorrect)");
|
||||||
|
|
||||||
// Build the new last closed ledger
|
// Build the new last closed ledger
|
||||||
auto buildLCL =
|
auto buildLCL =
|
||||||
@@ -765,7 +766,7 @@ RCLConsensus::Adaptor::buildLCL(
|
|||||||
|
|
||||||
// Set up to write SHAMap changes to our database,
|
// Set up to write SHAMap changes to our database,
|
||||||
// perform updates, extract changes
|
// perform updates, extract changes
|
||||||
JLOG(j_.debug()) << "Applying consensus set transactions to the"
|
JLOG(j_.debug()) << "Applying consensus txns transactions to the"
|
||||||
<< " last closed ledger";
|
<< " last closed ledger";
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -782,7 +783,7 @@ RCLConsensus::Adaptor::buildLCL(
|
|||||||
{
|
{
|
||||||
// Normal case, we are not replaying a ledger close
|
// Normal case, we are not replaying a ledger close
|
||||||
retriableTxs = applyTransactions(
|
retriableTxs = applyTransactions(
|
||||||
app_, set, accum, [&buildLCL](uint256 const& txID) {
|
app_, txns, accum, [&buildLCL](uint256 const& txID) {
|
||||||
return !buildLCL->txExists(txID);
|
return !buildLCL->txExists(txID);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -825,7 +826,9 @@ RCLConsensus::Adaptor::buildLCL(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, bool proposing)
|
RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger,
|
||||||
|
RCLTxSet const& txns,
|
||||||
|
bool proposing)
|
||||||
{
|
{
|
||||||
auto validationTime = app_.timeKeeper().closeTime();
|
auto validationTime = app_.timeKeeper().closeTime();
|
||||||
if (validationTime <= lastValidationTime_)
|
if (validationTime <= lastValidationTime_)
|
||||||
@@ -835,6 +838,7 @@ RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, bool proposing)
|
|||||||
// Build validation
|
// Build validation
|
||||||
auto v = std::make_shared<STValidation>(
|
auto v = std::make_shared<STValidation>(
|
||||||
ledger.id(),
|
ledger.id(),
|
||||||
|
txns.id(),
|
||||||
validationTime,
|
validationTime,
|
||||||
valPublic_,
|
valPublic_,
|
||||||
nodeID_,
|
nodeID_,
|
||||||
|
|||||||
@@ -218,10 +218,10 @@ class RCLConsensus
|
|||||||
|
|
||||||
/** Share the given tx set to peers.
|
/** Share the given tx set to peers.
|
||||||
|
|
||||||
@param set The TxSet to share.
|
@param txns The TxSet to share.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
share(RCLTxSet const& set);
|
share(RCLTxSet const& txns);
|
||||||
|
|
||||||
/** Get the ID of the previous ledger/last closed ledger(LCL) on the
|
/** Get the ID of the previous ledger/last closed ledger(LCL) on the
|
||||||
network
|
network
|
||||||
@@ -332,7 +332,7 @@ class RCLConsensus
|
|||||||
can be retried in the next round.
|
can be retried in the next round.
|
||||||
|
|
||||||
@param previousLedger Prior ledger building upon
|
@param previousLedger Prior ledger building upon
|
||||||
@param set The set of transactions to apply to the ledger
|
@param txns The set of transactions to apply to the ledger
|
||||||
@param closeTime The the ledger closed
|
@param closeTime The the ledger closed
|
||||||
@param closeTimeCorrect Whether consensus agreed on close time
|
@param closeTimeCorrect Whether consensus agreed on close time
|
||||||
@param closeResolution Resolution used to determine consensus close
|
@param closeResolution Resolution used to determine consensus close
|
||||||
@@ -345,7 +345,7 @@ class RCLConsensus
|
|||||||
RCLCxLedger
|
RCLCxLedger
|
||||||
buildLCL(
|
buildLCL(
|
||||||
RCLCxLedger const& previousLedger,
|
RCLCxLedger const& previousLedger,
|
||||||
RCLTxSet const& set,
|
RCLTxSet const& txns,
|
||||||
NetClock::time_point closeTime,
|
NetClock::time_point closeTime,
|
||||||
bool closeTimeCorrect,
|
bool closeTimeCorrect,
|
||||||
NetClock::duration closeResolution,
|
NetClock::duration closeResolution,
|
||||||
@@ -355,6 +355,7 @@ class RCLConsensus
|
|||||||
/** Validate the given ledger and share with peers as necessary
|
/** Validate the given ledger and share with peers as necessary
|
||||||
|
|
||||||
@param ledger The ledger to validate
|
@param ledger The ledger to validate
|
||||||
|
@param txns The consensus transaction set
|
||||||
@param proposing Whether we were proposing transactions while
|
@param proposing Whether we were proposing transactions while
|
||||||
generating this ledger. If we are not proposing,
|
generating this ledger. If we are not proposing,
|
||||||
a validation can still be sent to inform peers that
|
a validation can still be sent to inform peers that
|
||||||
@@ -362,7 +363,7 @@ class RCLConsensus
|
|||||||
but are still around and trying to catch up.
|
but are still around and trying to catch up.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
validate(RCLCxLedger const& ledger, bool proposing);
|
validate(RCLCxLedger const& ledger, RCLTxSet const& txns, bool proposing);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -312,9 +312,12 @@ leaves (SHAMap const& sm)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LedgerHistory::handleMismatch (
|
void
|
||||||
|
LedgerHistory::handleMismatch(
|
||||||
LedgerHash const& built,
|
LedgerHash const& built,
|
||||||
LedgerHash const& valid,
|
LedgerHash const& valid,
|
||||||
|
boost::optional<uint256> const& builtConsensusHash,
|
||||||
|
boost::optional<uint256> const& validatedConsensusHash,
|
||||||
Json::Value const& consensus)
|
Json::Value const& consensus)
|
||||||
{
|
{
|
||||||
assert (built != valid);
|
assert (built != valid);
|
||||||
@@ -357,6 +360,18 @@ void LedgerHistory::handleMismatch (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (builtConsensusHash && validatedConsensusHash)
|
||||||
|
{
|
||||||
|
if (builtConsensusHash != validatedConsensusHash)
|
||||||
|
JLOG(j_.error())
|
||||||
|
<< "MISMATCH on consensus transaction set "
|
||||||
|
<< " built: " << to_string(*builtConsensusHash)
|
||||||
|
<< " validated: " << to_string(*validatedConsensusHash);
|
||||||
|
else
|
||||||
|
JLOG(j_.error()) << "MISMATCH with same consensus transaction set: "
|
||||||
|
<< to_string(*builtConsensusHash);
|
||||||
|
}
|
||||||
|
|
||||||
// Find differences between built and valid ledgers
|
// Find differences between built and valid ledgers
|
||||||
auto const builtTx = leaves(builtLedger->txMap());
|
auto const builtTx = leaves(builtLedger->txMap());
|
||||||
auto const validTx = leaves(validLedger->txMap());
|
auto const validTx = leaves(validLedger->txMap());
|
||||||
@@ -409,6 +424,7 @@ void LedgerHistory::handleMismatch (
|
|||||||
|
|
||||||
void LedgerHistory::builtLedger (
|
void LedgerHistory::builtLedger (
|
||||||
std::shared_ptr<Ledger const> const& ledger,
|
std::shared_ptr<Ledger const> const& ledger,
|
||||||
|
uint256 const& consensusHash,
|
||||||
Json::Value consensus)
|
Json::Value consensus)
|
||||||
{
|
{
|
||||||
LedgerIndex index = ledger->info().seq;
|
LedgerIndex index = ledger->info().seq;
|
||||||
@@ -428,7 +444,12 @@ void LedgerHistory::builtLedger (
|
|||||||
JLOG (j_.error()) << "MISMATCH: seq=" << index
|
JLOG (j_.error()) << "MISMATCH: seq=" << index
|
||||||
<< " validated:" << entry->validated.get()
|
<< " validated:" << entry->validated.get()
|
||||||
<< " then:" << hash;
|
<< " then:" << hash;
|
||||||
handleMismatch (hash, entry->validated.get(), consensus);
|
handleMismatch(
|
||||||
|
hash,
|
||||||
|
entry->validated.get(),
|
||||||
|
consensusHash,
|
||||||
|
entry->validatedConsensusHash,
|
||||||
|
consensus);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -438,11 +459,13 @@ void LedgerHistory::builtLedger (
|
|||||||
}
|
}
|
||||||
|
|
||||||
entry->built.emplace (hash);
|
entry->built.emplace (hash);
|
||||||
|
entry->builtConsensusHash.emplace(consensusHash);
|
||||||
entry->consensus.emplace (std::move (consensus));
|
entry->consensus.emplace (std::move (consensus));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LedgerHistory::validatedLedger (
|
void LedgerHistory::validatedLedger (
|
||||||
std::shared_ptr<Ledger const> const& ledger)
|
std::shared_ptr<Ledger const> const& ledger,
|
||||||
|
boost::optional<uint256> const& consensusHash)
|
||||||
{
|
{
|
||||||
LedgerIndex index = ledger->info().seq;
|
LedgerIndex index = ledger->info().seq;
|
||||||
LedgerHash hash = ledger->info().hash;
|
LedgerHash hash = ledger->info().hash;
|
||||||
@@ -461,7 +484,12 @@ void LedgerHistory::validatedLedger (
|
|||||||
JLOG (j_.error()) << "MISMATCH: seq=" << index
|
JLOG (j_.error()) << "MISMATCH: seq=" << index
|
||||||
<< " built:" << entry->built.get()
|
<< " built:" << entry->built.get()
|
||||||
<< " then:" << hash;
|
<< " then:" << hash;
|
||||||
handleMismatch (entry->built.get(), hash, entry->consensus.get());
|
handleMismatch(
|
||||||
|
entry->built.get(),
|
||||||
|
hash,
|
||||||
|
entry->builtConsensusHash,
|
||||||
|
consensusHash,
|
||||||
|
entry->consensus.get());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -471,6 +499,7 @@ void LedgerHistory::validatedLedger (
|
|||||||
}
|
}
|
||||||
|
|
||||||
entry->validated.emplace (hash);
|
entry->validated.emplace (hash);
|
||||||
|
entry->validatedConsensusHash = consensusHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Ensure m_ledgers_by_hash doesn't have the wrong hash for a particular index
|
/** Ensure m_ledgers_by_hash doesn't have the wrong hash for a particular index
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public:
|
|||||||
return m_ledgers_by_hash.getHitRate ();
|
return m_ledgers_by_hash.getHitRate ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get a ledger given its squence number */
|
/** Get a ledger given its sequence number */
|
||||||
std::shared_ptr<Ledger const>
|
std::shared_ptr<Ledger const>
|
||||||
getLedgerBySeq (LedgerIndex ledgerIndex);
|
getLedgerBySeq (LedgerIndex ledgerIndex);
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
LedgerHash getLedgerHash (LedgerIndex ledgerIndex);
|
LedgerHash getLedgerHash (LedgerIndex ledgerIndex);
|
||||||
|
|
||||||
/** Set the history cache's paramters
|
/** Set the history cache's parameters
|
||||||
@param size The target size of the cache
|
@param size The target size of the cache
|
||||||
@param age The target age of the cache, in seconds
|
@param age The target age of the cache, in seconds
|
||||||
*/
|
*/
|
||||||
@@ -82,11 +82,13 @@ public:
|
|||||||
/** Report that we have locally built a particular ledger */
|
/** Report that we have locally built a particular ledger */
|
||||||
void builtLedger (
|
void builtLedger (
|
||||||
std::shared_ptr<Ledger const> const&,
|
std::shared_ptr<Ledger const> const&,
|
||||||
|
uint256 const& consensusHash,
|
||||||
Json::Value);
|
Json::Value);
|
||||||
|
|
||||||
/** Report that we have validated a particular ledger */
|
/** Report that we have validated a particular ledger */
|
||||||
void validatedLedger (
|
void validatedLedger (
|
||||||
std::shared_ptr<Ledger const> const&);
|
std::shared_ptr<Ledger const> const&,
|
||||||
|
boost::optional<uint256> const& consensusHash);
|
||||||
|
|
||||||
/** Repair a hash to index mapping
|
/** Repair a hash to index mapping
|
||||||
@param ledgerIndex The index whose mapping is to be repaired
|
@param ledgerIndex The index whose mapping is to be repaired
|
||||||
@@ -103,9 +105,18 @@ private:
|
|||||||
validate a different one.
|
validate a different one.
|
||||||
@param built The hash of the ledger we built
|
@param built The hash of the ledger we built
|
||||||
@param valid The hash of the ledger we deemed fully valid
|
@param valid The hash of the ledger we deemed fully valid
|
||||||
|
@param builtConsensusHash The hash of the consensus transaction for the
|
||||||
|
ledger we built
|
||||||
|
@param validatedConsensusHash The hash of the validated ledger's
|
||||||
|
consensus transaction set
|
||||||
@param consensus The status of the consensus round
|
@param consensus The status of the consensus round
|
||||||
*/
|
*/
|
||||||
void handleMismatch (LedgerHash const& built, LedgerHash const& valid,
|
void
|
||||||
|
handleMismatch(
|
||||||
|
LedgerHash const& built,
|
||||||
|
LedgerHash const& valid,
|
||||||
|
boost::optional<uint256> const& builtConsensusHash,
|
||||||
|
boost::optional<uint256> const& validatedConsensusHash,
|
||||||
Json::Value const& consensus);
|
Json::Value const& consensus);
|
||||||
|
|
||||||
Application& app_;
|
Application& app_;
|
||||||
@@ -120,8 +131,15 @@ private:
|
|||||||
// For debug and logging purposes
|
// For debug and logging purposes
|
||||||
struct cv_entry
|
struct cv_entry
|
||||||
{
|
{
|
||||||
|
// Hash of locally built ledger
|
||||||
boost::optional<LedgerHash> built;
|
boost::optional<LedgerHash> built;
|
||||||
|
// Hash of the validated ledger
|
||||||
boost::optional<LedgerHash> validated;
|
boost::optional<LedgerHash> validated;
|
||||||
|
// Hash of locally accepted consensus transaction set
|
||||||
|
boost::optional<uint256> builtConsensusHash;
|
||||||
|
// Hash of validated consensus transaction set
|
||||||
|
boost::optional<uint256> validatedConsensusHash;
|
||||||
|
// Consensus metadata of built ledger
|
||||||
boost::optional<Json::Value> consensus;
|
boost::optional<Json::Value> consensus;
|
||||||
};
|
};
|
||||||
using ConsensusValidated = TaggedCache <LedgerIndex, cv_entry>;
|
using ConsensusValidated = TaggedCache <LedgerIndex, cv_entry>;
|
||||||
|
|||||||
@@ -199,7 +199,11 @@ public:
|
|||||||
|
|
||||||
void checkAccept (std::shared_ptr<Ledger const> const& ledger);
|
void checkAccept (std::shared_ptr<Ledger const> const& ledger);
|
||||||
void checkAccept (uint256 const& hash, std::uint32_t seq);
|
void checkAccept (uint256 const& hash, std::uint32_t seq);
|
||||||
void consensusBuilt (std::shared_ptr<Ledger const> const& ledger, Json::Value consensus);
|
void
|
||||||
|
consensusBuilt(
|
||||||
|
std::shared_ptr<Ledger const> const& ledger,
|
||||||
|
uint256 const& consensusHash,
|
||||||
|
Json::Value consensus);
|
||||||
|
|
||||||
LedgerIndex getBuildingLedger ();
|
LedgerIndex getBuildingLedger ();
|
||||||
void setBuildingLedger (LedgerIndex index);
|
void setBuildingLedger (LedgerIndex index);
|
||||||
|
|||||||
@@ -182,12 +182,19 @@ void
|
|||||||
LedgerMaster::setValidLedger(
|
LedgerMaster::setValidLedger(
|
||||||
std::shared_ptr<Ledger const> const& l)
|
std::shared_ptr<Ledger const> const& l)
|
||||||
{
|
{
|
||||||
|
|
||||||
std::vector <NetClock::time_point> times;
|
std::vector <NetClock::time_point> times;
|
||||||
|
boost::optional<uint256> consensusHash;
|
||||||
|
|
||||||
if (! standalone_)
|
if (! standalone_)
|
||||||
{
|
{
|
||||||
times = app_.getValidations().getTrustedValidationTimes(
|
auto const vals = app_.getValidations().getTrustedForLedger(l->info().hash);
|
||||||
l->info().hash);
|
times.reserve(vals.size());
|
||||||
|
for(auto const& val: vals)
|
||||||
|
times.push_back(val->getSignTime());
|
||||||
|
|
||||||
|
if(!vals.empty())
|
||||||
|
consensusHash = vals.front()->getConsensusHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
NetClock::time_point signTime;
|
NetClock::time_point signTime;
|
||||||
@@ -216,7 +223,7 @@ LedgerMaster::setValidLedger(
|
|||||||
|
|
||||||
app_.getOPs().updateLocalTx (*l);
|
app_.getOPs().updateLocalTx (*l);
|
||||||
app_.getSHAMapStore().onLedgerClosed (getValidatedLedger());
|
app_.getSHAMapStore().onLedgerClosed (getValidatedLedger());
|
||||||
mLedgerHistory.validatedLedger (l);
|
mLedgerHistory.validatedLedger (l, consensusHash);
|
||||||
app_.getAmendmentTable().doValidatedLedger (l);
|
app_.getAmendmentTable().doValidatedLedger (l);
|
||||||
if (!app_.getOPs().isAmendmentBlocked() &&
|
if (!app_.getOPs().isAmendmentBlocked() &&
|
||||||
app_.getAmendmentTable().hasUnsupportedEnabled ())
|
app_.getAmendmentTable().hasUnsupportedEnabled ())
|
||||||
@@ -804,7 +811,9 @@ LedgerMaster::checkAccept (
|
|||||||
/** Report that the consensus process built a particular ledger */
|
/** Report that the consensus process built a particular ledger */
|
||||||
void
|
void
|
||||||
LedgerMaster::consensusBuilt(
|
LedgerMaster::consensusBuilt(
|
||||||
std::shared_ptr<Ledger const> const& ledger, Json::Value consensus)
|
std::shared_ptr<Ledger const> const& ledger,
|
||||||
|
uint256 const& consensusHash,
|
||||||
|
Json::Value consensus)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Because we just built a ledger, we are no longer building one
|
// Because we just built a ledger, we are no longer building one
|
||||||
@@ -814,7 +823,7 @@ LedgerMaster::consensusBuilt(
|
|||||||
if (standalone_)
|
if (standalone_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mLedgerHistory.builtLedger (ledger, std::move (consensus));
|
mLedgerHistory.builtLedger (ledger, consensusHash, std::move (consensus));
|
||||||
|
|
||||||
if (ledger->info().seq <= mValidLedgerSeq)
|
if (ledger->info().seq <= mValidLedgerSeq)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1167,8 +1167,8 @@ Consensus<Adaptor>::closeLedger()
|
|||||||
result_->roundTime.reset(clock_.now());
|
result_->roundTime.reset(clock_.now());
|
||||||
// Share the newly created transaction set if we haven't already
|
// Share the newly created transaction set if we haven't already
|
||||||
// received it from a peer
|
// received it from a peer
|
||||||
if (acquired_.emplace(result_->set.id(), result_->set).second)
|
if (acquired_.emplace(result_->txns.id(), result_->txns).second)
|
||||||
adaptor_.share(result_->set);
|
adaptor_.share(result_->txns);
|
||||||
|
|
||||||
if (mode_.get() == ConsensusMode::proposing)
|
if (mode_.get() == ConsensusMode::proposing)
|
||||||
adaptor_.propose(result_->position);
|
adaptor_.propose(result_->position);
|
||||||
@@ -1258,7 +1258,7 @@ Consensus<Adaptor>::updateOurPositions()
|
|||||||
parms))
|
parms))
|
||||||
{
|
{
|
||||||
if (!mutableSet)
|
if (!mutableSet)
|
||||||
mutableSet.emplace(result_->set);
|
mutableSet.emplace(result_->txns);
|
||||||
|
|
||||||
if (it.second.getOurVote())
|
if (it.second.getOurVote())
|
||||||
{
|
{
|
||||||
@@ -1352,14 +1352,14 @@ Consensus<Adaptor>::updateOurPositions()
|
|||||||
result_->position.isStale(ourCutoff)))
|
result_->position.isStale(ourCutoff)))
|
||||||
{
|
{
|
||||||
// close time changed or our position is stale
|
// close time changed or our position is stale
|
||||||
ourNewSet.emplace(result_->set);
|
ourNewSet.emplace(result_->txns);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ourNewSet)
|
if (ourNewSet)
|
||||||
{
|
{
|
||||||
auto newID = ourNewSet->id();
|
auto newID = ourNewSet->id();
|
||||||
|
|
||||||
result_->set = std::move(*ourNewSet);
|
result_->txns = std::move(*ourNewSet);
|
||||||
|
|
||||||
JLOG(j_.info()) << "Position change: CTime "
|
JLOG(j_.info()) << "Position change: CTime "
|
||||||
<< consensusCloseTime.time_since_epoch().count()
|
<< consensusCloseTime.time_since_epoch().count()
|
||||||
@@ -1369,16 +1369,16 @@ Consensus<Adaptor>::updateOurPositions()
|
|||||||
|
|
||||||
// Share our new transaction set and update disputes
|
// Share our new transaction set and update disputes
|
||||||
// if we haven't already received it
|
// if we haven't already received it
|
||||||
if (acquired_.emplace(newID, result_->set).second)
|
if (acquired_.emplace(newID, result_->txns).second)
|
||||||
{
|
{
|
||||||
if (!result_->position.isBowOut())
|
if (!result_->position.isBowOut())
|
||||||
adaptor_.share(result_->set);
|
adaptor_.share(result_->txns);
|
||||||
|
|
||||||
for (auto const& it : currPeerPositions_)
|
for (auto const& it : currPeerPositions_)
|
||||||
{
|
{
|
||||||
Proposal_t const& p = it.second.proposal();
|
Proposal_t const& p = it.second.proposal();
|
||||||
if (p.position() == newID)
|
if (p.position() == newID)
|
||||||
updateDisputes(it.first, result_->set);
|
updateDisputes(it.first, result_->txns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1479,13 +1479,13 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Nothing to dispute if we agree
|
// Nothing to dispute if we agree
|
||||||
if (result_->set.id() == o.id())
|
if (result_->txns.id() == o.id())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JLOG(j_.debug()) << "createDisputes " << result_->set.id() << " to "
|
JLOG(j_.debug()) << "createDisputes " << result_->txns.id() << " to "
|
||||||
<< o.id();
|
<< o.id();
|
||||||
|
|
||||||
auto differences = result_->set.compare(o);
|
auto differences = result_->txns.compare(o);
|
||||||
|
|
||||||
int dc = 0;
|
int dc = 0;
|
||||||
|
|
||||||
@@ -1494,10 +1494,10 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o)
|
|||||||
++dc;
|
++dc;
|
||||||
// create disputed transactions (from the ledger that has them)
|
// create disputed transactions (from the ledger that has them)
|
||||||
assert(
|
assert(
|
||||||
(id.second && result_->set.find(id.first) && !o.find(id.first)) ||
|
(id.second && result_->txns.find(id.first) && !o.find(id.first)) ||
|
||||||
(!id.second && !result_->set.find(id.first) && o.find(id.first)));
|
(!id.second && !result_->txns.find(id.first) && o.find(id.first)));
|
||||||
|
|
||||||
Tx_t tx = id.second ? *result_->set.find(id.first) : *o.find(id.first);
|
Tx_t tx = id.second ? *result_->txns.find(id.first) : *o.find(id.first);
|
||||||
auto txID = tx.id();
|
auto txID = tx.id();
|
||||||
|
|
||||||
if (result_->disputes.find(txID) != result_->disputes.end())
|
if (result_->disputes.find(txID) != result_->disputes.end())
|
||||||
@@ -1505,7 +1505,7 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o)
|
|||||||
|
|
||||||
JLOG(j_.debug()) << "Transaction " << txID << " is disputed";
|
JLOG(j_.debug()) << "Transaction " << txID << " is disputed";
|
||||||
|
|
||||||
typename Result::Dispute_t dtx{tx, result_->set.exists(txID),
|
typename Result::Dispute_t dtx{tx, result_->txns.exists(txID),
|
||||||
std::max(prevProposers_, currPeerPositions_.size()), j_};
|
std::max(prevProposers_, currPeerPositions_.size()), j_};
|
||||||
|
|
||||||
// Update all of the available peer's votes on the disputed transaction
|
// Update all of the available peer's votes on the disputed transaction
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ public:
|
|||||||
|
|
||||||
The initial consensus proposal from each peer has that peer's view of
|
The initial consensus proposal from each peer has that peer's view of
|
||||||
when the ledger closed. This object stores all those close times for
|
when the ledger closed. This object stores all those close times for
|
||||||
analysis of clock drift between peerss.
|
analysis of clock drift between peers.
|
||||||
*/
|
*/
|
||||||
struct ConsensusCloseTimes
|
struct ConsensusCloseTimes
|
||||||
{
|
{
|
||||||
@@ -210,13 +210,13 @@ struct ConsensusResult
|
|||||||
using Dispute_t = DisputedTx<Tx_t, NodeID_t>;
|
using Dispute_t = DisputedTx<Tx_t, NodeID_t>;
|
||||||
|
|
||||||
ConsensusResult(TxSet_t&& s, Proposal_t&& p)
|
ConsensusResult(TxSet_t&& s, Proposal_t&& p)
|
||||||
: set{std::move(s)}, position{std::move(p)}
|
: txns{std::move(s)}, position{std::move(p)}
|
||||||
{
|
{
|
||||||
assert(set.id() == position.position());
|
assert(txns.id() == position.position());
|
||||||
}
|
}
|
||||||
|
|
||||||
//! The set of transactions consensus agrees go in the ledger
|
//! The set of transactions consensus agrees go in the ledger
|
||||||
TxSet_t set;
|
TxSet_t txns;
|
||||||
|
|
||||||
//! Our proposed position on transactions/close time
|
//! Our proposed position on transactions/close time
|
||||||
Proposal_t position;
|
Proposal_t position;
|
||||||
|
|||||||
@@ -918,27 +918,6 @@ public:
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the sign times of all trusted full validations
|
|
||||||
|
|
||||||
@param ledgerID The identifier of ledger of interest
|
|
||||||
@return Vector of times
|
|
||||||
*/
|
|
||||||
std::vector<NetClock::time_point>
|
|
||||||
getTrustedValidationTimes(ID const& ledgerID)
|
|
||||||
{
|
|
||||||
std::vector<NetClock::time_point> times;
|
|
||||||
ScopedLock lock{mutex_};
|
|
||||||
byLedger(
|
|
||||||
lock,
|
|
||||||
ledgerID,
|
|
||||||
[&](std::size_t numValidations) { times.reserve(numValidations); },
|
|
||||||
[&](NodeID const&, Validation const& v) {
|
|
||||||
if (v.trusted() && v.full())
|
|
||||||
times.emplace_back(v.signTime());
|
|
||||||
});
|
|
||||||
return times;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns fees reported by trusted full validators in the given ledger
|
/** Returns fees reported by trusted full validators in the given ledger
|
||||||
|
|
||||||
@param ledgerID The identifier of ledger of interest
|
@param ledgerID The identifier of ledger of interest
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ public:
|
|||||||
signed before sharing with other nodes.
|
signed before sharing with other nodes.
|
||||||
|
|
||||||
@param ledgerHash The hash of the validated ledger
|
@param ledgerHash The hash of the validated ledger
|
||||||
|
@param consensusHash The hash of the consensus transaction set
|
||||||
@param signTime When the validation is signed
|
@param signTime When the validation is signed
|
||||||
@param publicKey The current signing public key
|
@param publicKey The current signing public key
|
||||||
@param nodeID ID corresponding to node's public master key
|
@param nodeID ID corresponding to node's public master key
|
||||||
@@ -97,6 +98,7 @@ public:
|
|||||||
|
|
||||||
STValidation(
|
STValidation(
|
||||||
uint256 const& ledgerHash,
|
uint256 const& ledgerHash,
|
||||||
|
uint256 const& consensusHash,
|
||||||
NetClock::time_point signTime,
|
NetClock::time_point signTime,
|
||||||
PublicKey const& publicKey,
|
PublicKey const& publicKey,
|
||||||
NodeID const& nodeID,
|
NodeID const& nodeID,
|
||||||
@@ -114,9 +116,14 @@ public:
|
|||||||
return emplace(n, buf, std::move(*this));
|
return emplace(n, buf, std::move(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash of the validated ledger
|
||||||
uint256
|
uint256
|
||||||
getLedgerHash() const;
|
getLedgerHash() const;
|
||||||
|
|
||||||
|
// Hash of consensus transaction set used to generate ledger
|
||||||
|
uint256
|
||||||
|
getConsensusHash() const;
|
||||||
|
|
||||||
NetClock::time_point
|
NetClock::time_point
|
||||||
getSignTime() const;
|
getSignTime() const;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ namespace ripple {
|
|||||||
|
|
||||||
STValidation::STValidation(
|
STValidation::STValidation(
|
||||||
uint256 const& ledgerHash,
|
uint256 const& ledgerHash,
|
||||||
|
uint256 const& consensusHash,
|
||||||
NetClock::time_point signTime,
|
NetClock::time_point signTime,
|
||||||
PublicKey const& publicKey,
|
PublicKey const& publicKey,
|
||||||
NodeID const& nodeID,
|
NodeID const& nodeID,
|
||||||
@@ -36,6 +37,7 @@ STValidation::STValidation(
|
|||||||
{
|
{
|
||||||
// Does not sign
|
// Does not sign
|
||||||
setFieldH256 (sfLedgerHash, ledgerHash);
|
setFieldH256 (sfLedgerHash, ledgerHash);
|
||||||
|
setFieldH256 (sfConsensusHash, consensusHash);
|
||||||
setFieldU32 (sfSigningTime, signTime.time_since_epoch().count());
|
setFieldU32 (sfSigningTime, signTime.time_since_epoch().count());
|
||||||
|
|
||||||
setFieldVL (sfSigningPubKey, publicKey.slice());
|
setFieldVL (sfSigningPubKey, publicKey.slice());
|
||||||
@@ -65,6 +67,11 @@ uint256 STValidation::getLedgerHash () const
|
|||||||
return getFieldH256 (sfLedgerHash);
|
return getFieldH256 (sfLedgerHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint256 STValidation::getConsensusHash () const
|
||||||
|
{
|
||||||
|
return getFieldH256 (sfConsensusHash);
|
||||||
|
}
|
||||||
|
|
||||||
NetClock::time_point
|
NetClock::time_point
|
||||||
STValidation::getSignTime () const
|
STValidation::getSignTime () const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -378,8 +378,8 @@ public:
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto const& val : validators)
|
for (auto const& val : validators)
|
||||||
{
|
{
|
||||||
auto v = std::make_shared <STValidation> (
|
auto v = std::make_shared<STValidation>(
|
||||||
uint256(), roundTime, val, calcNodeID(val), true);
|
uint256(), uint256(), roundTime, val, calcNodeID(val), true);
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
STVector256 field (sfAmendments);
|
STVector256 field (sfAmendments);
|
||||||
|
|||||||
252
src/test/app/LedgerHistory_test.cpp
Normal file
252
src/test/app/LedgerHistory_test.cpp
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2018 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 <BeastConfig.h>
|
||||||
|
#include <ripple/app/ledger/LedgerHistory.h>
|
||||||
|
#include <ripple/app/ledger/LedgerMaster.h>
|
||||||
|
#include <ripple/app/tx/apply.h>
|
||||||
|
#include <ripple/beast/insight/NullCollector.h>
|
||||||
|
#include <ripple/beast/unit_test.h>
|
||||||
|
#include <ripple/ledger/OpenView.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <test/jtx.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class LedgerHistory_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Log manager that searches for a specific message substring
|
||||||
|
*/
|
||||||
|
class CheckMessageLogs : public Logs
|
||||||
|
{
|
||||||
|
std::string msg_;
|
||||||
|
bool& found_;
|
||||||
|
|
||||||
|
class CheckMessageSink : public beast::Journal::Sink
|
||||||
|
{
|
||||||
|
CheckMessageLogs& owner_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CheckMessageSink(
|
||||||
|
beast::severities::Severity threshold,
|
||||||
|
CheckMessageLogs& owner)
|
||||||
|
: beast::Journal::Sink(threshold, false), owner_(owner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write(beast::severities::Severity level, std::string const& text)
|
||||||
|
override
|
||||||
|
{
|
||||||
|
if (text.find(owner_.msg_) != std::string::npos)
|
||||||
|
owner_.found_ = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Constructor
|
||||||
|
|
||||||
|
@param msg The message string to search for
|
||||||
|
@param found The variable to set to true if the message is found
|
||||||
|
*/
|
||||||
|
CheckMessageLogs(std::string msg, bool& found)
|
||||||
|
: Logs{beast::severities::kDebug}
|
||||||
|
, msg_{std::move(msg)}
|
||||||
|
, found_{found}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<beast::Journal::Sink>
|
||||||
|
makeSink(
|
||||||
|
std::string const& partition,
|
||||||
|
beast::severities::Severity threshold) override
|
||||||
|
{
|
||||||
|
return std::make_unique<CheckMessageSink>(threshold, *this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Generate a new ledger by hand, applying a specific close time offset
|
||||||
|
and optionally inserting a transaction.
|
||||||
|
|
||||||
|
If prev is nullptr, then the genesis ledger is made and no offset or
|
||||||
|
transaction is applied.
|
||||||
|
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<Ledger>
|
||||||
|
makeLedger(
|
||||||
|
std::shared_ptr<Ledger const> const& prev,
|
||||||
|
jtx::Env& env,
|
||||||
|
LedgerHistory& lh,
|
||||||
|
NetClock::duration closeOffset,
|
||||||
|
std::shared_ptr<STTx const> stx = {})
|
||||||
|
{
|
||||||
|
if (!prev)
|
||||||
|
{
|
||||||
|
assert(!stx);
|
||||||
|
return std::make_shared<Ledger>(
|
||||||
|
create_genesis,
|
||||||
|
env.app().config(),
|
||||||
|
std::vector<uint256>{},
|
||||||
|
env.app().family());
|
||||||
|
}
|
||||||
|
auto res = std::make_shared<Ledger>(
|
||||||
|
*prev, prev->info().closeTime + closeOffset);
|
||||||
|
|
||||||
|
if (stx)
|
||||||
|
{
|
||||||
|
OpenView accum(&*res);
|
||||||
|
applyTransaction(
|
||||||
|
env.app(), accum, *stx, false, tapNO_CHECK_SIGN, env.journal);
|
||||||
|
accum.apply(*res);
|
||||||
|
}
|
||||||
|
res->updateSkipList();
|
||||||
|
|
||||||
|
{
|
||||||
|
res->stateMap().flushDirty(hotACCOUNT_NODE, res->info().seq);
|
||||||
|
res->txMap().flushDirty(hotTRANSACTION_NODE, res->info().seq);
|
||||||
|
}
|
||||||
|
res->unshare();
|
||||||
|
|
||||||
|
// Accept ledger
|
||||||
|
res->setAccepted(
|
||||||
|
res->info().closeTime,
|
||||||
|
res->info().closeTimeResolution,
|
||||||
|
true /* close time correct*/,
|
||||||
|
env.app().config());
|
||||||
|
lh.insert(res, false);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testHandleMismatch()
|
||||||
|
{
|
||||||
|
testcase("LedgerHistory mismatch");
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// No mismatch
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
Env env{*this,
|
||||||
|
envconfig(),
|
||||||
|
std::make_unique<CheckMessageLogs>("MISMATCH ", found)};
|
||||||
|
LedgerHistory lh{beast::insight::NullCollector::New(), env.app()};
|
||||||
|
auto const genesis = makeLedger({}, env, lh, 0s);
|
||||||
|
uint256 const dummyTxHash{1};
|
||||||
|
lh.builtLedger(genesis, dummyTxHash, {});
|
||||||
|
lh.validatedLedger(genesis, dummyTxHash);
|
||||||
|
|
||||||
|
BEAST_EXPECT(!found);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close time mismatch
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
Env env{*this,
|
||||||
|
envconfig(),
|
||||||
|
std::make_unique<CheckMessageLogs>(
|
||||||
|
"MISMATCH on close time", found)};
|
||||||
|
LedgerHistory lh{beast::insight::NullCollector::New(), env.app()};
|
||||||
|
auto const genesis = makeLedger({}, env, lh, 0s);
|
||||||
|
auto const ledgerA = makeLedger(genesis, env, lh, 4s);
|
||||||
|
auto const ledgerB = makeLedger(genesis, env, lh, 40s);
|
||||||
|
|
||||||
|
uint256 const dummyTxHash{1};
|
||||||
|
lh.builtLedger(ledgerA, dummyTxHash, {});
|
||||||
|
lh.validatedLedger(ledgerB, dummyTxHash);
|
||||||
|
|
||||||
|
BEAST_EXPECT(found);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prior ledger mismatch
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
Env env{*this,
|
||||||
|
envconfig(),
|
||||||
|
std::make_unique<CheckMessageLogs>(
|
||||||
|
"MISMATCH on prior ledger", found)};
|
||||||
|
LedgerHistory lh{beast::insight::NullCollector::New(), env.app()};
|
||||||
|
auto const genesis = makeLedger({}, env, lh, 0s);
|
||||||
|
auto const ledgerA = makeLedger(genesis, env, lh, 4s);
|
||||||
|
auto const ledgerB = makeLedger(genesis, env, lh, 40s);
|
||||||
|
auto const ledgerAC = makeLedger(ledgerA, env, lh, 4s);
|
||||||
|
auto const ledgerBD = makeLedger(ledgerB, env, lh, 4s);
|
||||||
|
|
||||||
|
uint256 const dummyTxHash{1};
|
||||||
|
lh.builtLedger(ledgerAC, dummyTxHash, {});
|
||||||
|
lh.validatedLedger(ledgerBD, dummyTxHash);
|
||||||
|
|
||||||
|
BEAST_EXPECT(found);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate a bug in which consensus may agree on transactions, but
|
||||||
|
// somehow generate different ledgers
|
||||||
|
for (bool const txBug : {true, false})
|
||||||
|
{
|
||||||
|
std::string const msg = txBug
|
||||||
|
? "MISMATCH with same consensus transaction set"
|
||||||
|
: "MISMATCH on consensus transaction set";
|
||||||
|
bool found = false;
|
||||||
|
Env env{*this,
|
||||||
|
envconfig(),
|
||||||
|
std::make_unique<CheckMessageLogs>(msg, found)};
|
||||||
|
LedgerHistory lh{beast::insight::NullCollector::New(), env.app()};
|
||||||
|
|
||||||
|
Account alice{"A1"};
|
||||||
|
Account bob{"A2"};
|
||||||
|
env.fund(XRP(1000), alice, bob);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
auto const ledgerBase =
|
||||||
|
env.app().getLedgerMaster().getClosedLedger();
|
||||||
|
|
||||||
|
JTx txAlice = env.jt(noop(alice));
|
||||||
|
auto const ledgerA =
|
||||||
|
makeLedger(ledgerBase, env, lh, 4s, txAlice.stx);
|
||||||
|
|
||||||
|
JTx txBob = env.jt(noop(bob));
|
||||||
|
auto const ledgerB = makeLedger(ledgerBase, env, lh, 4s, txBob.stx);
|
||||||
|
|
||||||
|
lh.builtLedger(ledgerA, txAlice.stx->getTransactionID(), {});
|
||||||
|
// Simulate the bug by claiming ledgerB had the same consensus hash
|
||||||
|
// as ledgerA, but somehow generated different ledgers
|
||||||
|
lh.validatedLedger(
|
||||||
|
ledgerB,
|
||||||
|
txBug ? txAlice.stx->getTransactionID()
|
||||||
|
: txBob.stx->getTransactionID());
|
||||||
|
|
||||||
|
BEAST_EXPECT(found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
testHandleMismatch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(LedgerHistory, app, ripple);
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace ripple
|
||||||
@@ -36,8 +36,8 @@ class RCLValidations_test : public beast::unit_test::suite
|
|||||||
{
|
{
|
||||||
testcase("Change validation trusted status");
|
testcase("Change validation trusted status");
|
||||||
PublicKey key = derivePublicKey(KeyType::ed25519, randomSecretKey());
|
PublicKey key = derivePublicKey(KeyType::ed25519, randomSecretKey());
|
||||||
auto v = std::make_shared<STValidation>(
|
auto v = std::make_shared<STValidation>(uint256(), uint256(),
|
||||||
uint256(), NetClock::time_point(), key, calcNodeID(key), true);
|
NetClock::time_point(), key, calcNodeID(key), true);
|
||||||
|
|
||||||
BEAST_EXPECT(!v->isTrusted());
|
BEAST_EXPECT(!v->isTrusted());
|
||||||
v->setTrusted();
|
v->setTrusted();
|
||||||
|
|||||||
@@ -683,12 +683,10 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
sorted(harness.vals().getTrustedForLedger(id)) ==
|
sorted(harness.vals().getTrustedForLedger(id)) ==
|
||||||
sorted(expectedValidations));
|
sorted(expectedValidations));
|
||||||
|
|
||||||
std::vector<NetClock::time_point> expectedTimes;
|
|
||||||
std::uint32_t baseFee = 0;
|
std::uint32_t baseFee = 0;
|
||||||
std::vector<uint32_t> expectedFees;
|
std::vector<uint32_t> expectedFees;
|
||||||
for (auto const& val : expectedValidations)
|
for (auto const& val : expectedValidations)
|
||||||
{
|
{
|
||||||
expectedTimes.push_back(val.signTime());
|
|
||||||
expectedFees.push_back(val.loadFee().value_or(baseFee));
|
expectedFees.push_back(val.loadFee().value_or(baseFee));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,9 +694,6 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
sorted(harness.vals().fees(id, baseFee)) ==
|
sorted(harness.vals().fees(id, baseFee)) ==
|
||||||
sorted(expectedFees));
|
sorted(expectedFees));
|
||||||
|
|
||||||
BEAST_EXPECT(
|
|
||||||
sorted(harness.vals().getTrustedValidationTimes(id)) ==
|
|
||||||
sorted(expectedTimes));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -549,7 +549,7 @@ struct Peer
|
|||||||
const bool proposing = mode == ConsensusMode::proposing;
|
const bool proposing = mode == ConsensusMode::proposing;
|
||||||
const bool consensusFail = result.state == ConsensusState::MovedOn;
|
const bool consensusFail = result.state == ConsensusState::MovedOn;
|
||||||
|
|
||||||
TxSet const acceptedTxs = injectTxs(prevLedger, result.set);
|
TxSet const acceptedTxs = injectTxs(prevLedger, result.txns);
|
||||||
Ledger const newLedger = oracle.accept(
|
Ledger const newLedger = oracle.accept(
|
||||||
prevLedger,
|
prevLedger,
|
||||||
acceptedTxs.txs(),
|
acceptedTxs.txs(),
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ public:
|
|||||||
Validation const&
|
Validation const&
|
||||||
unwrap() const
|
unwrap() const
|
||||||
{
|
{
|
||||||
|
// For the rippled implementation in which RCLValidation wraps
|
||||||
|
// STValidation, the csf::Validation has no more specific type it
|
||||||
|
// wraps, so csf::Validation unwraps to itself
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <test/app/Flow_test.cpp>
|
#include <test/app/Flow_test.cpp>
|
||||||
#include <test/app/Freeze_test.cpp>
|
#include <test/app/Freeze_test.cpp>
|
||||||
#include <test/app/HashRouter_test.cpp>
|
#include <test/app/HashRouter_test.cpp>
|
||||||
|
#include <test/app/LedgerHistory_test.cpp>
|
||||||
#include <test/app/LedgerLoad_test.cpp>
|
#include <test/app/LedgerLoad_test.cpp>
|
||||||
#include <test/app/LoadFeeTrack_test.cpp>
|
#include <test/app/LoadFeeTrack_test.cpp>
|
||||||
#include <test/app/Manifest_test.cpp>
|
#include <test/app/Manifest_test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user