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>
<ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h">
</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">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>

View File

@@ -3873,6 +3873,12 @@
<ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h">
<Filter>ripple\rpc\impl</Filter>
</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">
<Filter>ripple\rpc\impl</Filter>
</ClCompile>

View File

@@ -246,18 +246,6 @@ public:
// 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 (
Ledger::ref lrLedger, RippleAddress const& accountID);
@@ -1130,64 +1118,6 @@ int NetworkOPsImp::findTransactionsByDestination (
// 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 (
Ledger::ref lrLedger, RippleAddress const& accountID)
{

View File

@@ -165,10 +165,6 @@ public:
// 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,
RippleAddress const& accountID) = 0;

View File

@@ -803,7 +803,7 @@ public:
{ "account_currencies", &RPCParser::parseAccountCurrencies, 1, 2 },
{ "account_info", &RPCParser::parseAccountItems, 1, 2 },
{ "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_tx", &RPCParser::parseAccountTransactions, 1, 8 },
{ "book_offers", &RPCParser::parseBookOffers, 2, 7 },

View File

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

View File

@@ -20,6 +20,11 @@
#include <BeastConfig.h>
#include <ripple/rpc/impl/Tuning.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/rpc/impl/GetAccountObjects.h>
#include <string>
#include <sstream>
#include <vector>
namespace ripple {
@@ -29,10 +34,12 @@ namespace ripple {
account_index: <integer> // optional, defaults to 0
ledger_hash: <string> // optional
ledger_index: <string | unsigned integer> // optional
type: <string> // optional, defaults to all account objects types
limit: <integer> // optional
marker: <opaque> // optional, resume previous query
}
*/
Json::Value doAccountObjects (RPC::Context& context)
{
auto const& params = context.params;
@@ -40,17 +47,17 @@ Json::Value doAccountObjects (RPC::Context& context)
return RPC::missing_field_error (jss::account);
Ledger::pointer ledger;
Json::Value result = RPC::lookupLedger (params, ledger, context.netOps);
auto result = RPC::lookupLedger (params, ledger, context.netOps);
if (ledger == nullptr)
return result;
RippleAddress rippleAddress;
RippleAddress raAccount;
{
bool bIndex;
std::string const strIdent = params[jss::account].asString ();
int const iIndex = context.params.isMember (jss::account_index)
auto const strIdent = params[jss::account].asString ();
auto iIndex = context.params.isMember (jss::account_index)
? 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);
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);
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))
{
auto const& jvLimit = params[jss::limit];
@@ -80,112 +119,37 @@ Json::Value doAccountObjects (RPC::Context& context)
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))
{
// 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];
auto const& marker = params[jss::marker];
if (! marker.isString ())
return RPC::expected_field_error (jss::marker, "string");
startAfter.SetHex (marker.asString ());
SLE::pointer sleObj = ledger->getSLEi (startAfter);
if (sleObj == nullptr)
return rpcError (rpcINVALID_PARAMS);
switch (sleObj->getType ())
{
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;
case ltOFFER:
startHint = sleObj->getFieldU64 (sfOwnerNode);
break;
default:
break;
}
std::stringstream ss (marker.asString ());
std::string s;
if (!std::getline(ss, s, ','))
return RPC::invalid_field_error (jss::marker);
if (! dirIndex.SetHex (s))
return RPC::invalid_field_error (jss::marker);
if (! std::getline (ss, s, ','))
return RPC::invalid_field_error (jss::marker);
// Caller provided the first object (startAfter), add it as first result
result[jss::account_objects].append (sleObj->getJson (0));
if (! entryIndex.SetHex (s))
return RPC::invalid_field_error (jss::marker);
}
else
if (! RPC::getAccountObjects (*ledger, raAccount.getAccountID (), type,
dirIndex, entryIndex, limit, result))
{
// We have no start point, limit should be one higher than requested.
++reserve;
return RPC::invalid_field_error (jss::marker);
}
Json::Value jv = Json::nullValue;
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 ();
result[jss::account] = raAccount.humanAccountID ();
context.loadType = Resource::feeMediumBurdenRPC;
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/Accounts.cpp>
#include <ripple/rpc/impl/GetAccountObjects.cpp>
#include <ripple/rpc/impl/Handler.cpp>
#include <ripple/rpc/impl/KeypairForSignature.cpp>
#include <ripple/rpc/impl/LegacyPathFind.cpp>