Skip to content

Commit

Permalink
Optimize account_lines and account_offers (RIPD-587)
Browse files Browse the repository at this point in the history
Conflicts:
	src/ripple/app/ledger/Ledger.h
  • Loading branch information
miguelportilla committed Sep 30, 2014
1 parent 2936bbf commit a556e92
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 177 deletions.
79 changes: 74 additions & 5 deletions src/ripple/app/ledger/Ledger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,67 @@ void Ledger::visitAccountItems (

}

bool Ledger::visitAccountItems (
Account const& accountID,
uint256 const& startAfter,
std::uint64_t const hint,
unsigned int limit,
std::function <bool (SLE::ref)> func) const
{
// Visit each item in this account's owner directory
uint256 const rootIndex (Ledger::getOwnerDirIndex (accountID));
uint256 currentIndex (rootIndex);

// If startAfter is not zero try jumping to that page using the hint
if (startAfter.isNonZero ())
{
uint256 const hintIndex (getDirNodeIndex (rootIndex, hint));
SLE::pointer hintDir (getSLEi (hintIndex));
if (hintDir != nullptr)
{
for (auto const& node : hintDir->getFieldV256 (sfIndexes))
{
if (node == startAfter)
{
// We found the hint, we can start here
currentIndex = hintIndex;
break;
}
}
}
}

bool found (false);

while (1)
{
SLE::pointer ownerDir (getSLEi (currentIndex));

if (!ownerDir || ownerDir->getType () != ltDIR_NODE)
return found;

for (auto const& node : ownerDir->getFieldV256 (sfIndexes))
{
if (!found)
{
if (startAfter.isZero () || node == startAfter)
found = true;
}
else if (func (getSLEi (node)) && limit-- <= 1)
{
return found;
}
}

std::uint64_t const uNodeNext (ownerDir->getFieldU64 (sfIndexNext));

if (uNodeNext == 0)
return found;

currentIndex = Ledger::getDirNodeIndex (rootIndex, uNodeNext);
}
}

static void visitHelper (
std::function<void (SLE::ref)>& function, SHAMapItem::ref item)
{
Expand Down Expand Up @@ -1784,12 +1845,20 @@ uint256 Ledger::getRippleStateIndex (
{
Serializer s (62);

bool const bAltB = a < b;
s.add16 (spaceRipple); // 2

if (a < b)
{
s.add160 (a); // 20
s.add160 (b); // 20
}
else
{
s.add160 (b); // 20
s.add160 (a); // 20
}

s.add16 (spaceRipple); // 2
s.add160 (bAltB ? a : b); // 20
s.add160 (bAltB ? b : a); // 20
s.add160 (currency); // 20
s.add160 (currency); // 20

return s.getSHA512Half ();
}
Expand Down
9 changes: 8 additions & 1 deletion src/ripple/app/ledger/Ledger.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,15 @@ class Ledger
SLE::pointer getAccountRoot (Account const& accountID) const;
SLE::pointer getAccountRoot (const RippleAddress & naAccountID) const;
void updateSkipList ();

void visitAccountItems (
Account const& acctID, std::function<void (SLE::ref)>) const;
Account const& accountID, std::function<void (SLE::ref)>) const;
bool visitAccountItems (
Account const& accountID,
uint256 const& startAfter, // Entry to start after
std::uint64_t const hint, // Hint which page to start at
unsigned int limit,
std::function <bool (SLE::ref)>) const;
void visitStateItems (std::function<void (SLE::ref)>) const;

// database functions (low-level)
Expand Down
223 changes: 133 additions & 90 deletions src/ripple/rpc/handlers/AccountLines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,49 @@

namespace ripple {

struct VisitData
{
std::vector <RippleState::pointer> items;
Account const& accountID;
RippleAddress const& rippleAddressPeer;
Account const& raPeerAccount;
};

void addLine (Json::Value& jsonLines, RippleState const& line)
{
STAmount const& saBalance (line.getBalance ());
STAmount const& saLimit (line.getLimit ());
STAmount const& saLimitPeer (line.getLimitPeer ());
Json::Value& jPeer (jsonLines.append (Json::objectValue));

jPeer[jss::account] = to_string (line.getAccountIDPeer ());
// Amount reported is positive if current account holds other
// account's IOUs.
//
// Amount reported is negative if other account holds current
// account's IOUs.
jPeer[jss::balance] = saBalance.getText ();
jPeer[jss::currency] = saBalance.getHumanCurrency ();
jPeer[jss::limit] = saLimit.getText ();
jPeer[jss::limit_peer] = saLimitPeer.getText ();
jPeer[jss::quality_in]
= static_cast<Json::UInt> (line.getQualityIn ());
jPeer[jss::quality_out]
= static_cast<Json::UInt> (line.getQualityOut ());
if (line.getAuth ())
jPeer[jss::authorized] = true;
if (line.getAuthPeer ())
jPeer[jss::peer_authorized] = true;
if (line.getNoRipple ())
jPeer[jss::no_ripple] = true;
if (line.getNoRipplePeer ())
jPeer[jss::no_ripple_peer] = true;
if (line.getFreeze ())
jPeer[jss::freeze] = true;
if (line.getFreezePeer ())
jPeer[jss::freeze_peer] = true;
}

// {
// account: <account>|<account_public_key>
// account_index: <number> // optional, defaults to 0.
Expand All @@ -34,48 +77,53 @@ Json::Value doAccountLines (RPC::Context& context)
auto& params = context.params_;

Ledger::pointer ledger;
Json::Value result = RPC::lookupLedger (params, ledger, context.netOps_);
Json::Value result (RPC::lookupLedger (params, ledger, context.netOps_));

if (!ledger)
if (! ledger)
return result;

if (!params.isMember (jss::account))
if (! params.isMember (jss::account))
return RPC::missing_field_error ("account");

std::string strIdent = params[jss::account].asString ();
bool bIndex = params.isMember (jss::account_index);
int iIndex = bIndex ? params[jss::account_index].asUInt () : 0;

RippleAddress raAccount;
std::string strIdent (params[jss::account].asString ());
bool bIndex (params.isMember (jss::account_index));
int iIndex (bIndex ? params[jss::account_index].asUInt () : 0);
RippleAddress rippleAddress;

result = RPC::accountFromString (
ledger, raAccount, bIndex, strIdent, iIndex, false, context.netOps_);
ledger, rippleAddress, bIndex, strIdent, iIndex, false, context.netOps_);

if (!result.empty ())
if (! result.empty ())
return result;

std::string strPeer = params.isMember (jss::peer)
? params[jss::peer].asString () : "";
bool bPeerIndex = params.isMember (jss::peer_index);
int iPeerIndex = bIndex ? params[jss::peer_index].asUInt () : 0;
if (! ledger->hasAccount (rippleAddress))
return rpcError (rpcACT_NOT_FOUND);

RippleAddress raPeer;
std::string strPeer (params.isMember (jss::peer)
? params[jss::peer].asString () : "");
bool bPeerIndex (params.isMember (jss::peer_index));
int iPeerIndex (bIndex ? params[jss::peer_index].asUInt () : 0);

if (!strPeer.empty ())
RippleAddress rippleAddressPeer;

if (! strPeer.empty ())
{
result[jss::peer] = raAccount.humanAccountID ();
result[jss::peer] = rippleAddress.humanAccountID ();

if (bPeerIndex)
result[jss::peer_index] = iPeerIndex;
result[jss::peer_index] = iPeerIndex;

result = RPC::accountFromString (
ledger, raPeer, bPeerIndex, strPeer, iPeerIndex, false,
context.netOps_);
result = RPC::accountFromString (ledger, rippleAddressPeer, bPeerIndex, strPeer,
iPeerIndex, false, context.netOps_);

if (!result.empty ())
if (! result.empty ())
return result;
}

Account raPeerAccount;
if (rippleAddressPeer.isValid ())
raPeerAccount = rippleAddressPeer.getAccountID ();

unsigned int limit;
if (params.isMember (jss::limit))
{
Expand All @@ -88,88 +136,83 @@ Json::Value doAccountLines (RPC::Context& context)
limit = RPC::Tuning::defaultLinesPerRequest;
}

RippleAddress resumeAddress;
Json::Value& jsonLines (result[jss::lines] = Json::arrayValue);
Account const& raAccount(rippleAddress.getAccountID ());
VisitData visitData = { {}, raAccount, rippleAddressPeer, raPeerAccount };
unsigned int reserve (limit);
uint256 startAfter;
std::uint64_t startHint;

if (params.isMember (jss::marker))
{
if (!resumeAddress.setAccountID (params[jss::marker].asString ()))
// We have a start point. Use limit - 1 from the result and use the
// very last one for the resume.
Json::Value const& marker (params[jss::marker]);

if (! marker.isString ())
return rpcError (rpcACT_MALFORMED);
}

if (ledger->hasAccount (raAccount))
startAfter.SetHex (marker.asString ());
SLE::pointer sleLine (ledger->getSLEi (startAfter));

if (sleLine == nullptr || sleLine->getType () != ltRIPPLE_STATE)
return rpcError (rpcINVALID_PARAMS);

if (sleLine->getFieldAmount (sfLowLimit).getIssuer () == raAccount)
startHint = sleLine->getFieldU64 (sfLowNode);
else if (sleLine->getFieldAmount (sfHighLimit).getIssuer () == raAccount)
startHint = sleLine->getFieldU64 (sfHighNode);
else
return rpcError (rpcINVALID_PARAMS);

// Caller provided the first line (startAfter), add it as first result
auto const line (RippleState::makeItem (raAccount, sleLine));
if (line == nullptr)
return rpcError (rpcINVALID_PARAMS);

addLine (jsonLines, *line);
visitData.items.reserve (reserve);
}
else
{
result[jss::account] = raAccount.humanAccountID ();
Json::Value& jsonLines = (result[jss::lines] = Json::arrayValue);

bool resume (! resumeAddress.isValid ());
unsigned int i (0);
startHint = 0;
// We have no start point, limit should be one higher than requested.
visitData.items.reserve (++reserve);
}

for (auto const& item : getRippleStateItems (raAccount.getAccountID (), ledger))
if (! ledger->visitAccountItems (raAccount, startAfter, startHint, reserve,
[&visitData](SLE::ref sleCur)
{
RippleState const& line (*item.get ());
Account const& lineAccount (line.getAccountIDPeer ());

if (! resume && resumeAddress.getAccountID () == lineAccount)
resume = true;

if (resume &&
(!raPeer.isValid () || raPeer.getAccountID () == lineAccount))
auto const line (RippleState::makeItem (visitData.accountID, sleCur));
if (line != nullptr &&
(! visitData.rippleAddressPeer.isValid () ||
visitData.raPeerAccount == line->getAccountIDPeer ()))
{
if (i < limit)
{
STAmount const& saBalance = line.getBalance ();
STAmount const& saLimit = line.getLimit ();
STAmount const& saLimitPeer = line.getLimitPeer ();

Json::Value& jPeer = jsonLines.append (Json::objectValue);

jPeer[jss::account] = to_string (lineAccount);
// Amount reported is positive if current account holds other
// account's IOUs.
//
// Amount reported is negative if other account holds current
// account's IOUs.
jPeer[jss::balance] = saBalance.getText ();
jPeer[jss::currency] = saBalance.getHumanCurrency ();
jPeer[jss::limit] = saLimit.getText ();
jPeer[jss::limit_peer] = saLimitPeer.getText ();
jPeer[jss::quality_in]
= static_cast<Json::UInt> (line.getQualityIn ());
jPeer[jss::quality_out]
= static_cast<Json::UInt> (line.getQualityOut ());
if (line.getAuth ())
jPeer[jss::authorized] = true;
if (line.getAuthPeer ())
jPeer[jss::peer_authorized] = true;
if (line.getNoRipple ())
jPeer[jss::no_ripple] = true;
if (line.getNoRipplePeer ())
jPeer[jss::no_ripple_peer] = true;
if (line.getFreeze ())
jPeer[jss::freeze] = true;
if (line.getFreezePeer ())
jPeer[jss::freeze_peer] = true;

++i;
}
else
{
result[jss::limit] = limit;
result[jss::marker] = to_string (lineAccount);
break;
}
visitData.items.emplace_back (line);
return true;
}
}

if (! resume)
return rpcError (rpcACT_MALFORMED);

context.loadType_ = Resource::feeMediumBurdenRPC;
return false;
}))
{
return rpcError (rpcINVALID_PARAMS);
}
else

if (visitData.items.size () == reserve)
{
result = rpcError (rpcACT_NOT_FOUND);
result[jss::limit] = limit;

RippleState::pointer line (visitData.items.back ());
result[jss::marker] = to_string (line->peekSLE ().getIndex ());
visitData.items.pop_back ();
}

result[jss::account] = rippleAddress.humanAccountID ();

for (auto const& item : visitData.items)
addLine (jsonLines, *item.get ());

context.loadType_ = Resource::feeMediumBurdenRPC;
return result;
}

Expand Down
Loading

0 comments on commit a556e92

Please sign in to comment.