Add filtering to Account Objects (RIPD-868)

This commit is contained in:
Miguel Portilla
2015-04-23 17:30:32 -04:00
committed by Vinnie Falco
parent e0ad66d967
commit ca07a1230b
10 changed files with 235 additions and 178 deletions

View File

@@ -3313,6 +3313,11 @@
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\GetAccountObjects.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\GetAccountObjects.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\Handler.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\Handler.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild> <ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile> </ClCompile>

View File

@@ -3873,6 +3873,12 @@
<ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\GetAccountObjects.cpp">
<Filter>ripple\rpc\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\GetAccountObjects.h">
<Filter>ripple\rpc\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\Handler.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\Handler.cpp">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClCompile> </ClCompile>

View File

@@ -246,18 +246,6 @@ public:
// Account functions // Account functions
// //
/** This method gathers all objects for an account in a ledger.
@param lpLedger Ledger to be searched for account objects.
@param accountID Account to find objects for.
@param startAfter Begin gathering account objects after this one.
@param hint Provides further granularity to startAfter.
@param limit Maximum number of objects to find.
@param func Function called on every object found.
*/
bool getAccountObjects (Ledger::pointer lpLedger,
Account const& accountID, uint256 const& startAfter,
std::uint64_t const hint, unsigned int limit,
std::function <bool (SLE::ref)> func) const override;
AccountState::pointer getAccountState ( AccountState::pointer getAccountState (
Ledger::ref lrLedger, RippleAddress const& accountID); Ledger::ref lrLedger, RippleAddress const& accountID);
@@ -1130,64 +1118,6 @@ int NetworkOPsImp::findTransactionsByDestination (
// Account functions // Account functions
// //
bool NetworkOPsImp::getAccountObjects (Ledger::pointer lpLedger,
Account const& accountID, uint256 const& startAfter,
std::uint64_t const hint, unsigned int limit,
std::function <bool (SLE::ref)> func) const
{
auto const rootIndex = getOwnerDirIndex (accountID);
auto currentIndex = rootIndex;
bool found = true;
if (startAfter.isNonZero ())
{
found = false;
auto const hintIndex = getDirNodeIndex (rootIndex, hint);
SLE::pointer hintDir = lpLedger->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;
}
}
}
}
for (;;)
{
auto const sleNode = lpLedger->getDirNode (currentIndex);
if (sleNode == nullptr)
return found;
for (auto const& uDirEntry : sleNode->getFieldV256(sfIndexes))
{
if (! found)
{
if (uDirEntry == startAfter)
found = true;
}
else if (func (lpLedger->getSLEi (uDirEntry)) && limit-- <= 1)
{
return true;
}
}
std::uint64_t const uNodeDir (sleNode->getFieldU64 (sfIndexNext));
if (uNodeDir == 0)
return found;
currentIndex = getDirNodeIndex (rootIndex, uNodeDir);
}
}
AccountState::pointer NetworkOPsImp::getAccountState ( AccountState::pointer NetworkOPsImp::getAccountState (
Ledger::ref lrLedger, RippleAddress const& accountID) Ledger::ref lrLedger, RippleAddress const& accountID)
{ {

View File

@@ -165,10 +165,6 @@ public:
// Account functions // Account functions
// //
virtual bool getAccountObjects(Ledger::pointer lpLedger,
Account const& accountID, uint256 const& startAfter,
std::uint64_t const hint, unsigned int limit,
std::function <bool (SLE::ref)> func) const = 0;
virtual AccountState::pointer getAccountState (Ledger::ref lrLedger, virtual AccountState::pointer getAccountState (Ledger::ref lrLedger,
RippleAddress const& accountID) = 0; RippleAddress const& accountID) = 0;

View File

@@ -803,7 +803,7 @@ public:
{ "account_currencies", &RPCParser::parseAccountCurrencies, 1, 2 }, { "account_currencies", &RPCParser::parseAccountCurrencies, 1, 2 },
{ "account_info", &RPCParser::parseAccountItems, 1, 2 }, { "account_info", &RPCParser::parseAccountItems, 1, 2 },
{ "account_lines", &RPCParser::parseAccountLines, 1, 5 }, { "account_lines", &RPCParser::parseAccountLines, 1, 5 },
{ "account_objects", &RPCParser::parseAccountItems, 1, 4 }, { "account_objects", &RPCParser::parseAccountItems, 1, 5 },
{ "account_offers", &RPCParser::parseAccountItems, 1, 4 }, { "account_offers", &RPCParser::parseAccountItems, 1, 4 },
{ "account_tx", &RPCParser::parseAccountTransactions, 1, 8 }, { "account_tx", &RPCParser::parseAccountTransactions, 1, 8 },
{ "book_offers", &RPCParser::parseBookOffers, 2, 7 }, { "book_offers", &RPCParser::parseBookOffers, 2, 7 },

View File

@@ -354,7 +354,8 @@ JSS ( tx_signing_hash ); // out: TransactionSign
JSS ( tx_unsigned ); // out: TransactionSign JSS ( tx_unsigned ); // out: TransactionSign
JSS ( txn_count ); // out: NetworkOPs JSS ( txn_count ); // out: NetworkOPs
JSS ( txs ); // out: TxHistory JSS ( txs ); // out: TxHistory
JSS ( type ); // rpc; out: NetworkOPs, LedgerEntrySet JSS ( type ); // in: AccountObjects
// out: NetworkOPs, LedgerEntrySet
// paths/Node.cpp, OverlayImpl, Logic // paths/Node.cpp, OverlayImpl, Logic
JSS ( type_hex ); // out: STPathSet JSS ( type_hex ); // out: STPathSet
JSS ( unl ); // out: UnlList JSS ( unl ); // out: UnlList

View File

@@ -20,6 +20,11 @@
#include <BeastConfig.h> #include <BeastConfig.h>
#include <ripple/rpc/impl/Tuning.h> #include <ripple/rpc/impl/Tuning.h>
#include <ripple/protocol/Indexes.h> #include <ripple/protocol/Indexes.h>
#include <ripple/rpc/impl/GetAccountObjects.h>
#include <string>
#include <sstream>
#include <vector>
namespace ripple { namespace ripple {
@@ -29,10 +34,12 @@ namespace ripple {
account_index: <integer> // optional, defaults to 0 account_index: <integer> // optional, defaults to 0
ledger_hash: <string> // optional ledger_hash: <string> // optional
ledger_index: <string | unsigned integer> // optional ledger_index: <string | unsigned integer> // optional
type: <string> // optional, defaults to all account objects types
limit: <integer> // optional limit: <integer> // optional
marker: <opaque> // optional, resume previous query marker: <opaque> // optional, resume previous query
} }
*/ */
Json::Value doAccountObjects (RPC::Context& context) Json::Value doAccountObjects (RPC::Context& context)
{ {
auto const& params = context.params; auto const& params = context.params;
@@ -40,17 +47,17 @@ Json::Value doAccountObjects (RPC::Context& context)
return RPC::missing_field_error (jss::account); return RPC::missing_field_error (jss::account);
Ledger::pointer ledger; Ledger::pointer ledger;
Json::Value result = RPC::lookupLedger (params, ledger, context.netOps); auto result = RPC::lookupLedger (params, ledger, context.netOps);
if (ledger == nullptr) if (ledger == nullptr)
return result; return result;
RippleAddress rippleAddress; RippleAddress raAccount;
{ {
bool bIndex; bool bIndex;
std::string const strIdent = params[jss::account].asString (); auto const strIdent = params[jss::account].asString ();
int const iIndex = context.params.isMember (jss::account_index) auto iIndex = context.params.isMember (jss::account_index)
? context.params[jss::account_index].asUInt () : 0; ? context.params[jss::account_index].asUInt () : 0;
Json::Value const jv = RPC::accountFromString(ledger, rippleAddress, bIndex, auto jv = RPC::accountFromString (ledger, raAccount, bIndex,
strIdent, iIndex, false, context.netOps); strIdent, iIndex, false, context.netOps);
if (! jv.empty ()) if (! jv.empty ())
{ {
@@ -61,10 +68,42 @@ Json::Value doAccountObjects (RPC::Context& context)
} }
} }
if (! ledger->hasAccount (rippleAddress)) if (! ledger->hasAccount (raAccount))
return rpcError (rpcACT_NOT_FOUND); return rpcError (rpcACT_NOT_FOUND);
unsigned int limit; auto type = ltINVALID;
if (params.isMember (jss::type))
{
using filter_types = std::vector <std::pair <std::string, LedgerEntryType>>;
static
beast::static_initializer <filter_types> const types ([]() -> filter_types {
return {
{ "account", ltACCOUNT_ROOT },
{ "amendments", ltAMENDMENTS },
{ "directory", ltDIR_NODE },
{ "fee", ltFEE_SETTINGS },
{ "hashes", ltLEDGER_HASHES },
{ "offer", ltOFFER },
{ "state", ltRIPPLE_STATE },
{ "ticket", ltTICKET }
};
}());
auto const& p = params[jss::type];
if (! p.isString ())
return RPC::expected_field_error (jss::type, "string");
auto const filter = p.asString ();
auto iter = std::find_if (types->begin (), types->end (),
[&filter](decltype (types->front ())& t)
{ return t.first == filter; });
if (iter == types->end ())
return RPC::invalid_field_error (jss::type);
type = iter->second;
}
auto limit = RPC::Tuning::defaultObjectsPerRequest;
if (params.isMember (jss::limit)) if (params.isMember (jss::limit))
{ {
auto const& jvLimit = params[jss::limit]; auto const& jvLimit = params[jss::limit];
@@ -80,112 +119,37 @@ Json::Value doAccountObjects (RPC::Context& context)
std::min (limit, RPC::Tuning::maxObjectsPerRequest)); std::min (limit, RPC::Tuning::maxObjectsPerRequest));
} }
} }
else
{
limit = RPC::Tuning::defaultObjectsPerRequest;
}
Account const& raAccount = rippleAddress.getAccountID ();
unsigned int reserve = limit;
uint256 startAfter;
std::uint64_t startHint = 0;
uint256 dirIndex;
uint256 entryIndex;
if (params.isMember (jss::marker)) if (params.isMember (jss::marker))
{ {
// We have a start point. Use limit - 1 from the result and use the auto const& marker = params[jss::marker];
// very last one for the resume.
Json::Value const& marker = params[jss::marker];
if (! marker.isString ()) if (! marker.isString ())
return RPC::expected_field_error (jss::marker, "string"); return RPC::expected_field_error (jss::marker, "string");
startAfter.SetHex (marker.asString ()); std::stringstream ss (marker.asString ());
SLE::pointer sleObj = ledger->getSLEi (startAfter); std::string s;
if (!std::getline(ss, s, ','))
return RPC::invalid_field_error (jss::marker);
if (sleObj == nullptr) if (! dirIndex.SetHex (s))
return rpcError (rpcINVALID_PARAMS); return RPC::invalid_field_error (jss::marker);
switch (sleObj->getType ()) if (! std::getline (ss, s, ','))
{ return RPC::invalid_field_error (jss::marker);
case ltRIPPLE_STATE:
if (sleObj->getFieldAmount (sfLowLimit).getIssuer () == raAccount)
startHint = sleObj->getFieldU64 (sfLowNode);
else if (sleObj->getFieldAmount (sfHighLimit).getIssuer () == raAccount)
startHint = sleObj->getFieldU64 (sfHighNode);
else
return rpcError (rpcINVALID_PARAMS);
break; if (! entryIndex.SetHex (s))
return RPC::invalid_field_error (jss::marker);
case ltOFFER:
startHint = sleObj->getFieldU64 (sfOwnerNode);
break;
default:
break;
} }
// Caller provided the first object (startAfter), add it as first result if (! RPC::getAccountObjects (*ledger, raAccount.getAccountID (), type,
result[jss::account_objects].append (sleObj->getJson (0)); dirIndex, entryIndex, limit, result))
}
else
{ {
// We have no start point, limit should be one higher than requested. return RPC::invalid_field_error (jss::marker);
++reserve;
} }
Json::Value jv = Json::nullValue; result[jss::account] = raAccount.humanAccountID ();
if (! context.netOps.getAccountObjects(ledger, raAccount, startAfter,
startHint, reserve, [&](SLE::ref sleCur)
{
if (! jv.isNull ())
result[jss::account_objects].append (jv);
switch (sleCur->getType ())
{
case ltRIPPLE_STATE:
case ltOFFER: // Deprecated
jv = sleCur->getJson (0);
return true;
case ltTICKET:
{
jv = sleCur->getJson (0);
Account const acc (sleCur->getFieldAccount160 (sfAccount));
uint32_t const seq (sleCur->getFieldU32 (sfSequence));
jv[jss::index] = to_string (getTicketIndex (acc, seq));
return true;
}
// case ltACCOUNT_ROOT:
// case ltDIR_NODE:
default:
if (! jv.isNull ())
jv = Json::nullValue;
return false;
}
}))
{
return rpcError (rpcINVALID_PARAMS);
}
if (! jv.isNull ())
{
if (result[jss::account_objects].size () == limit)
{
result[jss::limit] = limit;
result[jss::marker] = jv[jss::index];
}
else
{
result[jss::account_objects].append (jv);
}
}
result[jss::account] = rippleAddress.humanAccountID ();
context.loadType = Resource::feeMediumBurdenRPC; context.loadType = Resource::feeMediumBurdenRPC;
return result; return result;
} }

View File

@@ -0,0 +1,109 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2014 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/rpc/impl/GetAccountObjects.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/JsonFields.h>
namespace ripple {
namespace RPC {
bool
getAccountObjects (Ledger const& ledger, Account const& account,
LedgerEntryType const type, uint256 dirIndex, uint256 const& entryIndex,
std::uint32_t const limit, Json::Value& jvResult)
{
auto const rootDirIndex = getOwnerDirIndex (account);
auto found = false;
if (dirIndex.isZero ())
{
dirIndex = rootDirIndex;
found = true;
}
auto dir = ledger.getDirNode (dirIndex);
if (dir == nullptr)
return false;
std::uint32_t i = 0;
auto& jvObjects = jvResult[jss::account_objects];
for (;;)
{
auto const& entries = dir->getFieldV256 (sfIndexes);
auto iter = entries.begin ();
if (! found)
{
iter = std::find (iter, entries.end (), entryIndex);
if (iter == entries.end ())
return false;
found = true;
}
for (; iter != entries.end (); ++iter)
{
auto const sleNode = ledger.getSLEi (*iter);
if (type == ltINVALID || sleNode->getType () == type)
{
jvObjects.append (sleNode->getJson (0));
if (++i == limit)
{
if (++iter != entries.end ())
{
jvResult[jss::limit] = limit;
jvResult[jss::marker] = to_string (dirIndex) + ',' +
to_string (*iter);
return true;
}
break;
}
}
}
auto const nodeIndex = dir->getFieldU64 (sfIndexNext);
if (nodeIndex == 0)
return true;
dirIndex = getDirNodeIndex (rootDirIndex, nodeIndex);
dir = ledger.getDirNode (dirIndex);
if (dir == nullptr)
return true;
if (i == limit)
{
auto const& e = dir->getFieldV256 (sfIndexes);
if (! e.empty ())
{
jvResult[jss::limit] = limit;
jvResult[jss::marker] = to_string (dirIndex) + ',' +
to_string (*e.begin ());
}
return true;
}
}
}
} // RPC
} // ripple

View File

@@ -0,0 +1,45 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2014 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.
*/
//==============================================================================
#ifndef RIPPLE_RPC_ACCOUNT_OBJECTS_H_INCLUDED
#define RIPPLE_RPC_ACCOUNT_OBJECTS_H_INCLUDED
#include <ripple/app/ledger/Ledger.h>
namespace ripple {
namespace RPC {
/** Gathers all objects for an account in a ledger.
@param ledger Ledger to search account objects.
@param account Account to find objects for.
@param type Gathers objects of this type. ltINVALID gathers all types.
@param dirIndex Begin gathering account objects from this directory.
@param entryIndex Begin gathering objects from this directory node.
@param limit Maximum number of objects to find.
@param jvResult A JSON result that holds the request objects.
*/
bool
getAccountObjects (Ledger const& ledger, Account const& account,
LedgerEntryType const type, uint256 dirIndex, uint256 const& entryIndex,
std::uint32_t const limit, Json::Value& jvResult);
} // RPC
} // ripple
#endif

View File

@@ -94,6 +94,7 @@
#include <ripple/rpc/impl/AccountFromString.cpp> #include <ripple/rpc/impl/AccountFromString.cpp>
#include <ripple/rpc/impl/Accounts.cpp> #include <ripple/rpc/impl/Accounts.cpp>
#include <ripple/rpc/impl/GetAccountObjects.cpp>
#include <ripple/rpc/impl/Handler.cpp> #include <ripple/rpc/impl/Handler.cpp>
#include <ripple/rpc/impl/KeypairForSignature.cpp> #include <ripple/rpc/impl/KeypairForSignature.cpp>
#include <ripple/rpc/impl/LegacyPathFind.cpp> #include <ripple/rpc/impl/LegacyPathFind.cpp>