mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-29 15:35:50 +00:00
API for improved Unit Testing (RIPD-432):
* Added new test APIs allowing easy ways to create ledgers, apply transactions to them, close and advance them. * Moved Ledger tests from Ledger.cpp to Ledger.test.cpp. * Changed several TransactionEngine log priorities from lsINFO to lsDEBUG to reduce unnecessary verbosity in the log messages while running these tests. * Moved LedgerConsensus:applyTransactions from a private member function to a free function so that it could be accessed externally, and without having to reference a LedgerConsensus object. This was done to facilitate the new testing API.
This commit is contained in:
committed by
Nik Bougalis
parent
c62ccf4870
commit
eafa6f960f
@@ -1730,6 +1730,9 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\ledger\Ledger.h">
|
<ClInclude Include="..\..\src\ripple\app\ledger\Ledger.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\ledger\Ledger.test.cpp">
|
||||||
|
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\ledger\LedgerCleaner.cpp">
|
<ClCompile Include="..\..\src\ripple\app\ledger\LedgerCleaner.cpp">
|
||||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -2586,6 +2586,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\ledger\Ledger.h">
|
<ClInclude Include="..\..\src\ripple\app\ledger\Ledger.h">
|
||||||
<Filter>ripple\app\ledger</Filter>
|
<Filter>ripple\app\ledger</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\ledger\Ledger.test.cpp">
|
||||||
|
<Filter>ripple\app\ledger</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\ledger\LedgerCleaner.cpp">
|
<ClCompile Include="..\..\src\ripple\app\ledger\LedgerCleaner.cpp">
|
||||||
<Filter>ripple\app\ledger</Filter>
|
<Filter>ripple\app\ledger</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -1452,174 +1452,6 @@ private:
|
|||||||
msg, protocol::mtHAVE_SET)));
|
msg, protocol::mtHAVE_SET)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Apply a set of transactions to a ledger
|
|
||||||
|
|
||||||
@param set The set of transactions to apply
|
|
||||||
@param applyLedger The ledger to which the transactions should
|
|
||||||
be applied.
|
|
||||||
@param checkLedger A reference ledger for determining error
|
|
||||||
messages (typically new last closed ledger).
|
|
||||||
@param retriableTransactions collect failed transactions in this set
|
|
||||||
@param openLgr true if applyLedger is open, else false.
|
|
||||||
*/
|
|
||||||
void applyTransactions (SHAMap::ref set, Ledger::ref applyLedger,
|
|
||||||
Ledger::ref checkLedger, CanonicalTXSet& retriableTransactions,
|
|
||||||
bool openLgr)
|
|
||||||
{
|
|
||||||
TransactionEngine engine (applyLedger);
|
|
||||||
|
|
||||||
if (set)
|
|
||||||
{
|
|
||||||
for (SHAMapItem::pointer item = set->peekFirstItem (); !!item;
|
|
||||||
item = set->peekNextItem (item->getTag ()))
|
|
||||||
{
|
|
||||||
// If the checkLedger doesn't have the transaction
|
|
||||||
if (!checkLedger->hasTransaction (item->getTag ()))
|
|
||||||
{
|
|
||||||
// Then try to apply the transaction to applyLedger
|
|
||||||
WriteLog (lsINFO, LedgerConsensus) <<
|
|
||||||
"Processing candidate transaction: " << item->getTag ();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SerializerIterator sit (item->peekSerializer ());
|
|
||||||
STTx::pointer txn
|
|
||||||
= std::make_shared<STTx>(sit);
|
|
||||||
if (applyTransaction (engine, txn,
|
|
||||||
openLgr, true) == resultRetry)
|
|
||||||
{
|
|
||||||
// On failure, stash the failed transaction for
|
|
||||||
// later retry.
|
|
||||||
retriableTransactions.push_back (txn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
WriteLog (lsWARNING, LedgerConsensus) << " Throws";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int changes;
|
|
||||||
bool certainRetry = true;
|
|
||||||
// Attempt to apply all of the retriable transactions
|
|
||||||
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
|
|
||||||
{
|
|
||||||
WriteLog (lsDEBUG, LedgerConsensus) << "Pass: " << pass << " Txns: "
|
|
||||||
<< retriableTransactions.size ()
|
|
||||||
<< (certainRetry ? " retriable" : " final");
|
|
||||||
changes = 0;
|
|
||||||
|
|
||||||
auto it = retriableTransactions.begin ();
|
|
||||||
|
|
||||||
while (it != retriableTransactions.end ())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch (applyTransaction (engine, it->second,
|
|
||||||
openLgr, certainRetry))
|
|
||||||
{
|
|
||||||
case resultSuccess:
|
|
||||||
it = retriableTransactions.erase (it);
|
|
||||||
++changes;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case resultFail:
|
|
||||||
it = retriableTransactions.erase (it);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case resultRetry:
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
WriteLog (lsWARNING, LedgerConsensus)
|
|
||||||
<< "Transaction throws";
|
|
||||||
it = retriableTransactions.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteLog (lsDEBUG, LedgerConsensus) << "Pass: "
|
|
||||||
<< pass << " finished " << changes << " changes";
|
|
||||||
|
|
||||||
// A non-retry pass made no changes
|
|
||||||
if (!changes && !certainRetry)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Stop retriable passes
|
|
||||||
if ((!changes) || (pass >= LEDGER_RETRY_PASSES))
|
|
||||||
certainRetry = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are any transactions left, we must have
|
|
||||||
// tried them in at least one final pass
|
|
||||||
assert (retriableTransactions.empty() || !certainRetry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Apply a transaction to a ledger
|
|
||||||
|
|
||||||
@param engine The transaction engine containing the ledger.
|
|
||||||
@param txn The transaction to be applied to ledger.
|
|
||||||
@param openLedger true if ledger is open
|
|
||||||
@param retryAssured true if the transaction should be retried on failure.
|
|
||||||
@return One of resultSuccess, resultFail or resultRetry.
|
|
||||||
*/
|
|
||||||
int applyTransaction (TransactionEngine& engine
|
|
||||||
, STTx::ref txn, bool openLedger, bool retryAssured)
|
|
||||||
{
|
|
||||||
// Returns false if the transaction has need not be retried.
|
|
||||||
TransactionEngineParams parms = openLedger ? tapOPEN_LEDGER : tapNONE;
|
|
||||||
|
|
||||||
if (retryAssured)
|
|
||||||
{
|
|
||||||
parms = static_cast<TransactionEngineParams> (parms | tapRETRY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getApp().getHashRouter ().setFlag (txn->getTransactionID ()
|
|
||||||
, SF_SIGGOOD))
|
|
||||||
{
|
|
||||||
parms = static_cast<TransactionEngineParams>
|
|
||||||
(parms | tapNO_CHECK_SIGN);
|
|
||||||
}
|
|
||||||
WriteLog (lsDEBUG, LedgerConsensus) << "TXN "
|
|
||||||
<< txn->getTransactionID ()
|
|
||||||
<< (openLedger ? " open" : " closed")
|
|
||||||
<< (retryAssured ? "/retry" : "/final");
|
|
||||||
WriteLog (lsTRACE, LedgerConsensus) << txn->getJson (0);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool didApply;
|
|
||||||
TER result = engine.applyTransaction (*txn, parms, didApply);
|
|
||||||
|
|
||||||
if (didApply)
|
|
||||||
{
|
|
||||||
WriteLog (lsDEBUG, LedgerConsensus)
|
|
||||||
<< "Transaction success: " << transHuman (result);
|
|
||||||
return resultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTefFailure (result) || isTemMalformed (result) ||
|
|
||||||
isTelLocal (result))
|
|
||||||
{
|
|
||||||
// failure
|
|
||||||
WriteLog (lsDEBUG, LedgerConsensus)
|
|
||||||
<< "Transaction failure: " << transHuman (result);
|
|
||||||
return resultFail;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteLog (lsDEBUG, LedgerConsensus)
|
|
||||||
<< "Transaction retry: " << transHuman (result);
|
|
||||||
return resultRetry;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
WriteLog (lsWARNING, LedgerConsensus) << "Throws";
|
|
||||||
return resultFail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Round the close time to the close time resolution.
|
Round the close time to the close time resolution.
|
||||||
|
|
||||||
@@ -2164,4 +1996,173 @@ make_LedgerConsensus (LedgerConsensus::clock_type& clock, LocalTxs& localtx,
|
|||||||
prevLCLHash, previousLedger, closeTime, feeVote);
|
prevLCLHash, previousLedger, closeTime, feeVote);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Apply a transaction to a ledger
|
||||||
|
|
||||||
|
@param engine The transaction engine containing the ledger.
|
||||||
|
@param txn The transaction to be applied to ledger.
|
||||||
|
@param openLedger true if ledger is open
|
||||||
|
@param retryAssured true if the transaction should be retried on failure.
|
||||||
|
@return One of resultSuccess, resultFail or resultRetry.
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
int applyTransaction (TransactionEngine& engine
|
||||||
|
, STTx::ref txn, bool openLedger, bool retryAssured)
|
||||||
|
{
|
||||||
|
// Returns false if the transaction has need not be retried.
|
||||||
|
TransactionEngineParams parms = openLedger ? tapOPEN_LEDGER : tapNONE;
|
||||||
|
|
||||||
|
if (retryAssured)
|
||||||
|
{
|
||||||
|
parms = static_cast<TransactionEngineParams> (parms | tapRETRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getApp().getHashRouter ().setFlag (txn->getTransactionID ()
|
||||||
|
, SF_SIGGOOD))
|
||||||
|
{
|
||||||
|
parms = static_cast<TransactionEngineParams>
|
||||||
|
(parms | tapNO_CHECK_SIGN);
|
||||||
|
}
|
||||||
|
WriteLog (lsDEBUG, LedgerConsensus) << "TXN "
|
||||||
|
<< txn->getTransactionID ()
|
||||||
|
<< (openLedger ? " open" : " closed")
|
||||||
|
<< (retryAssured ? "/retry" : "/final");
|
||||||
|
WriteLog (lsTRACE, LedgerConsensus) << txn->getJson (0);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool didApply;
|
||||||
|
TER result = engine.applyTransaction (*txn, parms, didApply);
|
||||||
|
|
||||||
|
if (didApply)
|
||||||
|
{
|
||||||
|
WriteLog (lsDEBUG, LedgerConsensus)
|
||||||
|
<< "Transaction success: " << transHuman (result);
|
||||||
|
return LedgerConsensusImp::resultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTefFailure (result) || isTemMalformed (result) ||
|
||||||
|
isTelLocal (result))
|
||||||
|
{
|
||||||
|
// failure
|
||||||
|
WriteLog (lsDEBUG, LedgerConsensus)
|
||||||
|
<< "Transaction failure: " << transHuman (result);
|
||||||
|
return LedgerConsensusImp::resultFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLog (lsDEBUG, LedgerConsensus)
|
||||||
|
<< "Transaction retry: " << transHuman (result);
|
||||||
|
return LedgerConsensusImp::resultRetry;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
WriteLog (lsWARNING, LedgerConsensus) << "Throws";
|
||||||
|
return LedgerConsensusImp::resultFail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Apply a set of transactions to a ledger
|
||||||
|
|
||||||
|
@param set The set of transactions to apply
|
||||||
|
@param applyLedger The ledger to which the transactions should
|
||||||
|
be applied.
|
||||||
|
@param checkLedger A reference ledger for determining error
|
||||||
|
messages (typically new last closed ledger).
|
||||||
|
@param retriableTransactions collect failed transactions in this set
|
||||||
|
@param openLgr true if applyLedger is open, else false.
|
||||||
|
*/
|
||||||
|
void applyTransactions (SHAMap::ref set, Ledger::ref applyLedger,
|
||||||
|
Ledger::ref checkLedger, CanonicalTXSet& retriableTransactions,
|
||||||
|
bool openLgr)
|
||||||
|
{
|
||||||
|
TransactionEngine engine (applyLedger);
|
||||||
|
|
||||||
|
if (set)
|
||||||
|
{
|
||||||
|
for (SHAMapItem::pointer item = set->peekFirstItem (); !!item;
|
||||||
|
item = set->peekNextItem (item->getTag ()))
|
||||||
|
{
|
||||||
|
// If the checkLedger doesn't have the transaction
|
||||||
|
if (!checkLedger->hasTransaction (item->getTag ()))
|
||||||
|
{
|
||||||
|
// Then try to apply the transaction to applyLedger
|
||||||
|
WriteLog (lsINFO, LedgerConsensus) <<
|
||||||
|
"Processing candidate transaction: " << item->getTag ();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SerializerIterator sit (item->peekSerializer ());
|
||||||
|
STTx::pointer txn
|
||||||
|
= std::make_shared<STTx>(sit);
|
||||||
|
if (applyTransaction (engine, txn,
|
||||||
|
openLgr, true) == LedgerConsensusImp::resultRetry)
|
||||||
|
{
|
||||||
|
// On failure, stash the failed transaction for
|
||||||
|
// later retry.
|
||||||
|
retriableTransactions.push_back (txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
WriteLog (lsWARNING, LedgerConsensus) << " Throws";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int changes;
|
||||||
|
bool certainRetry = true;
|
||||||
|
// Attempt to apply all of the retriable transactions
|
||||||
|
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
|
||||||
|
{
|
||||||
|
WriteLog (lsDEBUG, LedgerConsensus) << "Pass: " << pass << " Txns: "
|
||||||
|
<< retriableTransactions.size ()
|
||||||
|
<< (certainRetry ? " retriable" : " final");
|
||||||
|
changes = 0;
|
||||||
|
|
||||||
|
auto it = retriableTransactions.begin ();
|
||||||
|
|
||||||
|
while (it != retriableTransactions.end ())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (applyTransaction (engine, it->second,
|
||||||
|
openLgr, certainRetry))
|
||||||
|
{
|
||||||
|
case LedgerConsensusImp::resultSuccess:
|
||||||
|
it = retriableTransactions.erase (it);
|
||||||
|
++changes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LedgerConsensusImp::resultFail:
|
||||||
|
it = retriableTransactions.erase (it);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LedgerConsensusImp::resultRetry:
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
WriteLog (lsWARNING, LedgerConsensus)
|
||||||
|
<< "Transaction throws";
|
||||||
|
it = retriableTransactions.erase (it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLog (lsDEBUG, LedgerConsensus) << "Pass: "
|
||||||
|
<< pass << " finished " << changes << " changes";
|
||||||
|
|
||||||
|
// A non-retry pass made no changes
|
||||||
|
if (!changes && !certainRetry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Stop retriable passes
|
||||||
|
if ((!changes) || (pass >= LEDGER_RETRY_PASSES))
|
||||||
|
certainRetry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are any transactions left, we must have
|
||||||
|
// tried them in at least one final pass
|
||||||
|
assert (retriableTransactions.empty() || !certainRetry);
|
||||||
|
}
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <ripple/app/ledger/Ledger.h>
|
#include <ripple/app/ledger/Ledger.h>
|
||||||
#include <ripple/app/ledger/LedgerProposal.h>
|
#include <ripple/app/ledger/LedgerProposal.h>
|
||||||
|
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||||
#include <ripple/app/misc/FeeVote.h>
|
#include <ripple/app/misc/FeeVote.h>
|
||||||
#include <ripple/app/tx/LocalTxs.h>
|
#include <ripple/app/tx/LocalTxs.h>
|
||||||
#include <ripple/json/json_value.h>
|
#include <ripple/json/json_value.h>
|
||||||
@@ -95,6 +96,11 @@ make_LedgerConsensus (LedgerConsensus::clock_type& clock, LocalTxs& localtx,
|
|||||||
LedgerHash const & prevLCLHash, Ledger::ref previousLedger,
|
LedgerHash const & prevLCLHash, Ledger::ref previousLedger,
|
||||||
std::uint32_t closeTime, FeeVote& feeVote);
|
std::uint32_t closeTime, FeeVote& feeVote);
|
||||||
|
|
||||||
|
void
|
||||||
|
applyTransactions(SHAMap::ref set, Ledger::ref applyLedger,
|
||||||
|
Ledger::ref checkLedger,
|
||||||
|
CanonicalTXSet& retriableTransactions, bool openLgr);
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1899,43 +1899,6 @@ std::vector<uint256> Ledger::getNeededAccountStateHashes (
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class Ledger_test : public beast::unit_test::suite
|
|
||||||
{
|
|
||||||
void test_genesis_ledger ()
|
|
||||||
{
|
|
||||||
RippleAddress rootSeedMaster
|
|
||||||
= RippleAddress::createSeedGeneric ("masterpassphrase");
|
|
||||||
RippleAddress rootGeneratorMaster
|
|
||||||
= RippleAddress::createGeneratorPublic (rootSeedMaster);
|
|
||||||
RippleAddress rootAddress
|
|
||||||
= RippleAddress::createAccountPublic (rootGeneratorMaster, 0);
|
|
||||||
std::uint64_t startAmount (100000);
|
|
||||||
Ledger::pointer ledger (std::make_shared <Ledger> (
|
|
||||||
rootAddress, startAmount));
|
|
||||||
ledger->updateHash();
|
|
||||||
expect(ledger->assertSane());
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_getQuality ()
|
|
||||||
{
|
|
||||||
uint256 uBig (
|
|
||||||
"D2DC44E5DC189318DB36EF87D2104CDF0A0FE3A4B698BEEE55038D7EA4C68000");
|
|
||||||
|
|
||||||
// VFALCO NOTE This fails in the original version as well.
|
|
||||||
expect (6125895493223874560 == getQuality (uBig));
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
void run ()
|
|
||||||
{
|
|
||||||
test_genesis_ledger ();
|
|
||||||
test_getQuality ();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(Ledger,ripple_app,ripple);
|
|
||||||
|
|
||||||
Ledger::StaticLockType Ledger::sPendingSaveLock;
|
Ledger::StaticLockType Ledger::sPendingSaveLock;
|
||||||
std::set<std::uint32_t> Ledger::sPendingSaves;
|
std::set<std::uint32_t> Ledger::sPendingSaves;
|
||||||
|
|
||||||
|
|||||||
318
src/ripple/app/ledger/Ledger.test.cpp
Normal file
318
src/ripple/app/ledger/Ledger.test.cpp
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/ledger/Ledger.h>
|
||||||
|
#include <ripple/app/transactors/Transactor.h>
|
||||||
|
#include <ripple/basics/seconds_clock.h>
|
||||||
|
#include <ripple/protocol/RippleAddress.h>
|
||||||
|
#include <ripple/protocol/STParsedJSON.h>
|
||||||
|
#include <ripple/protocol/TxFlags.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
class Ledger_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
using TestAccount = std::pair<RippleAddress, unsigned>;
|
||||||
|
|
||||||
|
struct Amount
|
||||||
|
{
|
||||||
|
double value;
|
||||||
|
std::string currency;
|
||||||
|
TestAccount issuer;
|
||||||
|
|
||||||
|
Json::Value
|
||||||
|
getJson() const
|
||||||
|
{
|
||||||
|
Json::Value tx_json;
|
||||||
|
tx_json["currency"] = currency;
|
||||||
|
tx_json["issuer"] = issuer.first.humanAccountID();
|
||||||
|
tx_json["value"] = std::to_string(value);
|
||||||
|
return tx_json;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to parse a transaction in Json, sign it with account,
|
||||||
|
// and return it as a STTx
|
||||||
|
STTx
|
||||||
|
parseTransaction(TestAccount& account, Json::Value const& tx_json)
|
||||||
|
{
|
||||||
|
STParsedJSONObject parsed("tx_json", tx_json);
|
||||||
|
std::unique_ptr<STObject> sopTrans = std::move(parsed.object);
|
||||||
|
expect(sopTrans != nullptr);
|
||||||
|
sopTrans->setFieldVL(sfSigningPubKey, account.first.getAccountPublic());
|
||||||
|
return STTx{*sopTrans};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to apply a transaction to a ledger
|
||||||
|
void
|
||||||
|
applyTransaction(Ledger::pointer const& ledger, STTx const& tx)
|
||||||
|
{
|
||||||
|
TransactionEngine engine(ledger);
|
||||||
|
bool didApply = false;
|
||||||
|
auto r = engine.applyTransaction(tx, tapOPEN_LEDGER | tapNO_CHECK_SIGN,
|
||||||
|
didApply);
|
||||||
|
expect(r == tesSUCCESS);
|
||||||
|
expect(didApply);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create genesis ledger from a start amount in drops, and the public
|
||||||
|
// master RippleAddress
|
||||||
|
Ledger::pointer
|
||||||
|
createGenesisLedger(std::uint64_t start_amount_drops, TestAccount const& master)
|
||||||
|
{
|
||||||
|
Ledger::pointer ledger = std::make_shared<Ledger>(master.first,
|
||||||
|
start_amount_drops);
|
||||||
|
ledger->updateHash();
|
||||||
|
ledger->setClosed();
|
||||||
|
expect(ledger->assertSane());
|
||||||
|
return ledger;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an account represented by public RippleAddress and private
|
||||||
|
// RippleAddress
|
||||||
|
TestAccount
|
||||||
|
createAccount()
|
||||||
|
{
|
||||||
|
static RippleAddress const seed
|
||||||
|
= RippleAddress::createSeedGeneric ("masterpassphrase");
|
||||||
|
static RippleAddress const generator
|
||||||
|
= RippleAddress::createGeneratorPublic (seed);
|
||||||
|
static int iSeq = -1;
|
||||||
|
++iSeq;
|
||||||
|
return std::make_pair(RippleAddress::createAccountPublic(generator, iSeq),
|
||||||
|
std::uint64_t(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
freezeAccount(TestAccount& account, Ledger::pointer const& ledger)
|
||||||
|
{
|
||||||
|
Json::Value tx_json;
|
||||||
|
tx_json["TransactionType"] = "AccountSet";
|
||||||
|
tx_json["Fee"] = std::to_string(10);
|
||||||
|
tx_json["Account"] = account.first.humanAccountID();
|
||||||
|
tx_json["SetFlag"] = asfGlobalFreeze;
|
||||||
|
tx_json["Sequence"] = ++account.second;
|
||||||
|
STTx tx = parseTransaction(account, tx_json);
|
||||||
|
applyTransaction(ledger, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unfreezeAccount(TestAccount& account, Ledger::pointer const& ledger)
|
||||||
|
{
|
||||||
|
Json::Value tx_json;
|
||||||
|
tx_json["TransactionType"] = "AccountSet";
|
||||||
|
tx_json["Fee"] = std::to_string(10);
|
||||||
|
tx_json["Account"] = account.first.humanAccountID();
|
||||||
|
tx_json["ClearFlag"] = asfGlobalFreeze;
|
||||||
|
tx_json["Sequence"] = ++account.second;
|
||||||
|
STTx tx = parseTransaction(account, tx_json);
|
||||||
|
applyTransaction(ledger, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
makePayment(TestAccount& from, TestAccount const& to,
|
||||||
|
std::uint64_t amountDrops,
|
||||||
|
Ledger::pointer const& ledger)
|
||||||
|
{
|
||||||
|
Json::Value tx_json;
|
||||||
|
tx_json["Account"] = from.first.humanAccountID();
|
||||||
|
tx_json["Amount"] = std::to_string(amountDrops);
|
||||||
|
tx_json["Destination"] = to.first.humanAccountID();
|
||||||
|
tx_json["TransactionType"] = "Payment";
|
||||||
|
tx_json["Fee"] = std::to_string(10);
|
||||||
|
tx_json["Sequence"] = ++from.second;
|
||||||
|
tx_json["Flags"] = tfUniversal;
|
||||||
|
STTx tx = parseTransaction(from, tx_json);
|
||||||
|
applyTransaction(ledger, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
makePayment(TestAccount& from, TestAccount const& to,
|
||||||
|
std::string const& currency, std::string const& amount,
|
||||||
|
Ledger::pointer const& ledger)
|
||||||
|
{
|
||||||
|
Json::Value tx_json;
|
||||||
|
tx_json["Account"] = from.first.humanAccountID();
|
||||||
|
tx_json["Amount"] = Amount{std::stod(amount), currency, to}.getJson();
|
||||||
|
tx_json["Destination"] = to.first.humanAccountID();
|
||||||
|
tx_json["TransactionType"] = "Payment";
|
||||||
|
tx_json["Fee"] = std::to_string(10);
|
||||||
|
tx_json["Sequence"] = ++from.second;
|
||||||
|
tx_json["Flags"] = tfUniversal;
|
||||||
|
STTx tx = parseTransaction(from, tx_json);
|
||||||
|
applyTransaction(ledger, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
createOffer(TestAccount& from, Amount const& in, Amount const& out,
|
||||||
|
Ledger::pointer ledger)
|
||||||
|
{
|
||||||
|
Json::Value tx_json;
|
||||||
|
tx_json["TransactionType"] = "OfferCreate";
|
||||||
|
tx_json["Fee"] = std::to_string(10);
|
||||||
|
tx_json["Account"] = from.first.humanAccountID();
|
||||||
|
tx_json["TakerPays"] = in.getJson();
|
||||||
|
tx_json["TakerGets"] = out.getJson();
|
||||||
|
tx_json["Sequence"] = ++from.second;
|
||||||
|
STTx tx = parseTransaction(from, tx_json);
|
||||||
|
applyTransaction(ledger, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// As currently implemented, this will cancel only the last offer made
|
||||||
|
// from this account.
|
||||||
|
void
|
||||||
|
cancelOffer(TestAccount& from, Ledger::pointer ledger)
|
||||||
|
{
|
||||||
|
Json::Value tx_json;
|
||||||
|
tx_json["TransactionType"] = "OfferCancel";
|
||||||
|
tx_json["Fee"] = std::to_string(10);
|
||||||
|
tx_json["Account"] = from.first.humanAccountID();
|
||||||
|
tx_json["OfferSequence"] = from.second;
|
||||||
|
tx_json["Sequence"] = ++from.second;
|
||||||
|
STTx tx = parseTransaction(from, tx_json);
|
||||||
|
applyTransaction(ledger, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
makeTrustSet(TestAccount& from, TestAccount const& issuer,
|
||||||
|
std::string const& currency, double amount,
|
||||||
|
Ledger::pointer const& ledger)
|
||||||
|
{
|
||||||
|
Json::Value tx_json;
|
||||||
|
tx_json["Account"] = from.first.humanAccountID();
|
||||||
|
Json::Value& limitAmount = tx_json["LimitAmount"];
|
||||||
|
limitAmount["currency"] = currency;
|
||||||
|
limitAmount["issuer"] = issuer.first.humanAccountID();
|
||||||
|
limitAmount["value"] = std::to_string(amount);
|
||||||
|
tx_json["TransactionType"] = "TrustSet";
|
||||||
|
tx_json["Fee"] = std::to_string(10);
|
||||||
|
tx_json["Sequence"] = ++from.second;
|
||||||
|
tx_json["Flags"] = tfClearNoRipple;
|
||||||
|
STTx tx = parseTransaction(from, tx_json);
|
||||||
|
applyTransaction(ledger, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ledger::pointer
|
||||||
|
close_and_advance(Ledger::pointer ledger, Ledger::pointer LCL)
|
||||||
|
{
|
||||||
|
SHAMap::pointer set = ledger->peekTransactionMap();
|
||||||
|
CanonicalTXSet retriableTransactions{set->getHash()};
|
||||||
|
Ledger::pointer newLCL = std::make_shared<Ledger>(false, *LCL);
|
||||||
|
// Set up to write SHAMap changes to our database,
|
||||||
|
// perform updates, extract changes
|
||||||
|
applyTransactions(set, newLCL, newLCL, retriableTransactions, false);
|
||||||
|
newLCL->updateSkipList();
|
||||||
|
newLCL->setClosed();
|
||||||
|
int asf = newLCL->peekAccountStateMap()->flushDirty(hotACCOUNT_NODE,
|
||||||
|
newLCL->getLedgerSeq());
|
||||||
|
int tmf = newLCL->peekTransactionMap()->flushDirty(hotTRANSACTION_NODE,
|
||||||
|
newLCL->getLedgerSeq());
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto const epoch_offset = days{10957}; // 2000-01-01
|
||||||
|
std::uint32_t closeTime = time_point_cast<seconds> // now
|
||||||
|
(system_clock::now()-epoch_offset).
|
||||||
|
time_since_epoch().count();
|
||||||
|
int CloseResolution = seconds{LEDGER_TIME_ACCURACY}.count();
|
||||||
|
bool closeTimeCorrect = true;
|
||||||
|
newLCL->setAccepted(closeTime, CloseResolution, closeTimeCorrect);
|
||||||
|
return newLCL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_genesisLedger ()
|
||||||
|
{
|
||||||
|
std::uint64_t const xrp = std::mega::num;
|
||||||
|
|
||||||
|
// Create master account
|
||||||
|
auto master = createAccount();
|
||||||
|
|
||||||
|
// Create genesis ledger
|
||||||
|
Ledger::pointer LCL = createGenesisLedger(100000*xrp, master);
|
||||||
|
|
||||||
|
// Create open scratch ledger
|
||||||
|
Ledger::pointer ledger = std::make_shared<Ledger>(false, *LCL);
|
||||||
|
|
||||||
|
// Create user accounts
|
||||||
|
auto gw1 = createAccount();
|
||||||
|
auto gw2 = createAccount();
|
||||||
|
auto gw3 = createAccount();
|
||||||
|
auto alice = createAccount();
|
||||||
|
auto mark = createAccount();
|
||||||
|
|
||||||
|
// Fund gw1, gw2, gw3, alice, mark from master
|
||||||
|
makePayment(master, gw1, 5000*xrp, ledger);
|
||||||
|
makePayment(master, gw2, 4000*xrp, ledger);
|
||||||
|
makePayment(master, gw3, 3000*xrp, ledger);
|
||||||
|
makePayment(master, alice, 2000*xrp, ledger);
|
||||||
|
makePayment(master, mark, 1000*xrp, ledger);
|
||||||
|
|
||||||
|
LCL = close_and_advance(ledger, LCL);
|
||||||
|
ledger = std::make_shared<Ledger>(false, *LCL);
|
||||||
|
|
||||||
|
// alice trusts FOO/gw1
|
||||||
|
makeTrustSet(alice, gw1, "FOO", 1, ledger);
|
||||||
|
|
||||||
|
// mark trusts FOO/gw2
|
||||||
|
makeTrustSet(mark, gw2, "FOO", 1, ledger);
|
||||||
|
|
||||||
|
// mark trusts FOO/gw3
|
||||||
|
makeTrustSet(mark, gw3, "FOO", 1, ledger);
|
||||||
|
|
||||||
|
// gw2 pays mark with FOO
|
||||||
|
makePayment(gw2, mark, "FOO", ".1", ledger);
|
||||||
|
|
||||||
|
// gw3 pays mark with FOO
|
||||||
|
makePayment(gw3, mark, "FOO", ".2", ledger);
|
||||||
|
|
||||||
|
// gw1 pays alice with FOO
|
||||||
|
makePayment(gw1, alice, "FOO", ".3", ledger);
|
||||||
|
|
||||||
|
LCL = close_and_advance(ledger, LCL);
|
||||||
|
ledger = std::make_shared<Ledger>(false, *LCL);
|
||||||
|
|
||||||
|
createOffer(mark, Amount{1, "FOO", gw1}, Amount{1, "FOO", gw2}, ledger);
|
||||||
|
createOffer(mark, Amount{1, "FOO", gw2}, Amount{1, "FOO", gw3}, ledger);
|
||||||
|
cancelOffer(mark, ledger);
|
||||||
|
freezeAccount(alice, ledger);
|
||||||
|
|
||||||
|
LCL = close_and_advance(ledger, LCL);
|
||||||
|
ledger = std::make_shared<Ledger>(false, *LCL);
|
||||||
|
|
||||||
|
makePayment(alice, mark, 1*xrp, ledger);
|
||||||
|
|
||||||
|
LCL = close_and_advance(ledger, LCL);
|
||||||
|
ledger = std::make_shared<Ledger>(false, *LCL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_getQuality ()
|
||||||
|
{
|
||||||
|
uint256 uBig (
|
||||||
|
"D2DC44E5DC189318DB36EF87D2104CDF0A0FE3A4B698BEEE55038D7EA4C68000");
|
||||||
|
expect (6125895493223874560 == getQuality (uBig));
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
void run ()
|
||||||
|
{
|
||||||
|
test_genesisLedger ();
|
||||||
|
test_getQuality ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(Ledger,ripple_app,ripple);
|
||||||
|
|
||||||
|
} // ripple
|
||||||
@@ -42,7 +42,7 @@ void TransactionEngine::txnWrite ()
|
|||||||
|
|
||||||
case taaCREATE:
|
case taaCREATE:
|
||||||
{
|
{
|
||||||
WriteLog (lsINFO, TransactionEngine) << "applyTransaction: taaCREATE: " << sleEntry->getText ();
|
WriteLog (lsDEBUG, TransactionEngine) << "applyTransaction: taaCREATE: " << sleEntry->getText ();
|
||||||
|
|
||||||
if (mLedger->writeBack (lepCREATE, sleEntry) & lepERROR)
|
if (mLedger->writeBack (lepCREATE, sleEntry) & lepERROR)
|
||||||
assert (false);
|
assert (false);
|
||||||
@@ -51,7 +51,7 @@ void TransactionEngine::txnWrite ()
|
|||||||
|
|
||||||
case taaMODIFY:
|
case taaMODIFY:
|
||||||
{
|
{
|
||||||
WriteLog (lsINFO, TransactionEngine) << "applyTransaction: taaMODIFY: " << sleEntry->getText ();
|
WriteLog (lsDEBUG, TransactionEngine) << "applyTransaction: taaMODIFY: " << sleEntry->getText ();
|
||||||
|
|
||||||
if (mLedger->writeBack (lepNONE, sleEntry) & lepERROR)
|
if (mLedger->writeBack (lepNONE, sleEntry) & lepERROR)
|
||||||
assert (false);
|
assert (false);
|
||||||
@@ -60,7 +60,7 @@ void TransactionEngine::txnWrite ()
|
|||||||
|
|
||||||
case taaDELETE:
|
case taaDELETE:
|
||||||
{
|
{
|
||||||
WriteLog (lsINFO, TransactionEngine) << "applyTransaction: taaDELETE: " << sleEntry->getText ();
|
WriteLog (lsDEBUG, TransactionEngine) << "applyTransaction: taaDELETE: " << sleEntry->getText ();
|
||||||
|
|
||||||
if (!mLedger->peekAccountStateMap ()->delItem (it.first))
|
if (!mLedger->peekAccountStateMap ()->delItem (it.first))
|
||||||
assert (false);
|
assert (false);
|
||||||
@@ -117,14 +117,14 @@ TER TransactionEngine::applyTransaction (
|
|||||||
return temUNKNOWN;
|
return temUNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShouldLog (lsINFO, TransactionEngine))
|
if (ShouldLog (lsDEBUG, TransactionEngine))
|
||||||
{
|
{
|
||||||
std::string strToken;
|
std::string strToken;
|
||||||
std::string strHuman;
|
std::string strHuman;
|
||||||
|
|
||||||
transResultInfo (terResult, strToken, strHuman);
|
transResultInfo (terResult, strToken, strHuman);
|
||||||
|
|
||||||
WriteLog (lsINFO, TransactionEngine) <<
|
WriteLog (lsDEBUG, TransactionEngine) <<
|
||||||
"applyTransaction: terResult=" << strToken <<
|
"applyTransaction: terResult=" << strToken <<
|
||||||
" : " << terResult <<
|
" : " << terResult <<
|
||||||
" : " << strHuman;
|
" : " << strHuman;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <ripple/unity/app.h>
|
#include <ripple/unity/app.h>
|
||||||
|
|
||||||
#include <ripple/app/ledger/Ledger.cpp>
|
#include <ripple/app/ledger/Ledger.cpp>
|
||||||
|
#include <ripple/app/ledger/Ledger.test.cpp>
|
||||||
#include <ripple/app/shamap/SHAMapDelta.cpp>
|
#include <ripple/app/shamap/SHAMapDelta.cpp>
|
||||||
#include <ripple/app/shamap/SHAMapNodeID.cpp>
|
#include <ripple/app/shamap/SHAMapNodeID.cpp>
|
||||||
#include <ripple/app/shamap/SHAMapTreeNode.cpp>
|
#include <ripple/app/shamap/SHAMapTreeNode.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user