mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-28 23:15:52 +00:00
RPC account_objects (RIPD-777)
General RPC command that can retrieve objects in the account root. * Add account objects integration test. * Support tickets. * Add removeElement in Json::Value
This commit is contained in:
committed by
Vinnie Falco
parent
0b5582ed0d
commit
4d0ed3d857
@@ -3130,6 +3130,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\AccountLines.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\AccountObjects.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\AccountOffers.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
||||
@@ -3681,6 +3681,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\AccountLines.cpp">
|
||||
<Filter>ripple\rpc\handlers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\AccountObjects.cpp">
|
||||
<Filter>ripple\rpc\handlers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\AccountOffers.cpp">
|
||||
<Filter>ripple\rpc\handlers</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -246,6 +246,18 @@ 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);
|
||||
SLE::pointer getGenerator (
|
||||
@@ -1121,6 +1133,64 @@ 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)
|
||||
{
|
||||
|
||||
@@ -165,6 +165,10 @@ 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;
|
||||
virtual SLE::pointer getGenerator (Ledger::ref lrLedger,
|
||||
|
||||
@@ -813,6 +813,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_offers", &RPCParser::parseAccountItems, 1, 4 },
|
||||
{ "account_tx", &RPCParser::parseAccountTransactions, 1, 8 },
|
||||
{ "book_offers", &RPCParser::parseBookOffers, 2, 7 },
|
||||
|
||||
@@ -59,8 +59,10 @@ JSS ( account_data ); // out: AccountInfo
|
||||
JSS ( account_hash ); // out: LedgerToJson
|
||||
JSS ( account_id ); // out: WalletPropose
|
||||
JSS ( account_index ); // in: AccountCurrencies, AccountOffers,
|
||||
// AccountInfo, AccountLines, OwnerInfo
|
||||
// AccountInfo, AccountLines,
|
||||
// AccountObjects, OwnerInfo
|
||||
// out: AccountOffers
|
||||
JSS ( account_objects ); // out: AccountObjects
|
||||
JSS ( account_root ); // in: LedgerEntry
|
||||
JSS ( accounts ); // in: LedgerEntry, Subscribe,
|
||||
// handlers/Ledger, Unsubscribe
|
||||
@@ -200,7 +202,7 @@ JSS ( ledger_min ); // in, out: AccountTx*
|
||||
JSS ( ledger_time ); // out: NetworkOPs
|
||||
JSS ( levels ); // LogLevels
|
||||
JSS ( limit ); // in/out: AccountTx*, AccountOffers,
|
||||
// AccountLines;
|
||||
// AccountLines, AccountObjects
|
||||
// in: LedgerData, BookOffers
|
||||
JSS ( limit_peer ); // out: AccountLines
|
||||
JSS ( lines ); // out: AccountLines
|
||||
@@ -214,7 +216,8 @@ JSS ( load_fee ); // out: LoadFeeTrackImp
|
||||
JSS ( local ); // out: resource/Logic.h
|
||||
JSS ( local_txs ); // out: GetCounts
|
||||
JSS ( marker ); // in/out: AccountTx, AccountOffers,
|
||||
// AccountLines, LedgerData
|
||||
// AccountLines, AccountObjects,
|
||||
// LedgerData
|
||||
// in: BookOffers
|
||||
JSS ( master_key ); // out: WalletPropose
|
||||
JSS ( master_seed ); // out: WalletPropose
|
||||
|
||||
194
src/ripple/rpc/handlers/AccountObjects.cpp
Normal file
194
src/ripple/rpc/handlers/AccountObjects.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 <BeastConfig.h>
|
||||
#include <ripple/rpc/impl/Tuning.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** General RPC command that can retrieve objects in the account root.
|
||||
{
|
||||
account: <account>|<account_public_key>
|
||||
account_index: <integer> // optional, defaults to 0
|
||||
ledger_hash: <string> // optional
|
||||
ledger_index: <string | unsigned integer> // optional
|
||||
limit: <integer> // optional
|
||||
marker: <opaque> // optional, resume previous query
|
||||
}
|
||||
*/
|
||||
Json::Value doAccountObjects (RPC::Context& context)
|
||||
{
|
||||
auto const& params = context.params;
|
||||
if (! params.isMember (jss::account))
|
||||
return RPC::missing_field_error (jss::account);
|
||||
|
||||
Ledger::pointer ledger;
|
||||
Json::Value result = RPC::lookupLedger (params, ledger, context.netOps);
|
||||
if (ledger == nullptr)
|
||||
return result;
|
||||
|
||||
RippleAddress rippleAddress;
|
||||
{
|
||||
bool bIndex;
|
||||
std::string const strIdent = params[jss::account].asString ();
|
||||
int const iIndex = context.params.isMember (jss::account_index)
|
||||
? context.params[jss::account_index].asUInt () : 0;
|
||||
Json::Value const jv = RPC::accountFromString(ledger, rippleAddress, bIndex,
|
||||
strIdent, iIndex, false, context.netOps);
|
||||
if (! jv.empty ())
|
||||
{
|
||||
for (auto it = jv.begin (); it != jv.end (); ++it)
|
||||
result[it.memberName ()] = it.key ();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (! ledger->hasAccount (rippleAddress))
|
||||
return rpcError (rpcACT_NOT_FOUND);
|
||||
|
||||
unsigned int limit;
|
||||
if (params.isMember (jss::limit))
|
||||
{
|
||||
auto const& jvLimit = params[jss::limit];
|
||||
if (! jvLimit.isIntegral ())
|
||||
return RPC::expected_field_error (jss::limit, "unsigned integer");
|
||||
|
||||
limit = jvLimit.isUInt () ? jvLimit.asUInt () :
|
||||
std::max (0, jvLimit.asInt ());
|
||||
|
||||
if (context.role != Role::ADMIN)
|
||||
{
|
||||
limit = std::max (RPC::Tuning::minObjectsPerRequest,
|
||||
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;
|
||||
|
||||
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];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Caller provided the first object (startAfter), add it as first result
|
||||
result[jss::account_objects].append (sleObj->getJson (0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have no start point, limit should be one higher than requested.
|
||||
++reserve;
|
||||
}
|
||||
|
||||
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:
|
||||
// case ltGENERATOR_MAP:
|
||||
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;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
@@ -25,6 +25,7 @@ namespace ripple {
|
||||
Json::Value doAccountCurrencies (RPC::Context&);
|
||||
Json::Value doAccountInfo (RPC::Context&);
|
||||
Json::Value doAccountLines (RPC::Context&);
|
||||
Json::Value doAccountObjects (RPC::Context&);
|
||||
Json::Value doAccountOffers (RPC::Context&);
|
||||
Json::Value doAccountTx (RPC::Context&);
|
||||
Json::Value doAccountTxSwitch (RPC::Context&);
|
||||
|
||||
@@ -100,6 +100,7 @@ HandlerTable HANDLERS({
|
||||
{ "account_info", byRef (&doAccountInfo), Role::USER, NEEDS_CURRENT_LEDGER },
|
||||
{ "account_currencies", byRef (&doAccountCurrencies), Role::USER, NEEDS_CURRENT_LEDGER },
|
||||
{ "account_lines", byRef (&doAccountLines), Role::USER, NEEDS_CURRENT_LEDGER },
|
||||
{ "account_objects", byRef (&doAccountObjects), Role::USER, NEEDS_CURRENT_LEDGER },
|
||||
{ "account_offers", byRef (&doAccountOffers), Role::USER, NEEDS_CURRENT_LEDGER },
|
||||
{ "account_tx", byRef (&doAccountTxSwitch), Role::USER, NEEDS_NETWORK_CONNECTION },
|
||||
{ "blacklist", byRef (&doBlackList), Role::ADMIN, NO_CONDITION },
|
||||
|
||||
@@ -27,42 +27,57 @@ namespace RPC {
|
||||
/** @{ */
|
||||
namespace Tuning {
|
||||
|
||||
/** Default account lines return per request to the
|
||||
account_lines command when no limit param is specified
|
||||
/** Default account objects returned per request from the
|
||||
account_objects command when no limit param is specified
|
||||
*/
|
||||
unsigned int const defaultLinesPerRequest (200);
|
||||
static unsigned int const defaultObjectsPerRequest (200);
|
||||
|
||||
/** Minimum account lines return per request to the
|
||||
account_lines command. Specified in the limit param.
|
||||
/** Minimum account objects returned per request from the
|
||||
account_objects command. Specified in the limit param.
|
||||
*/
|
||||
unsigned int const minLinesPerRequest (10);
|
||||
static unsigned int const minObjectsPerRequest (10);
|
||||
|
||||
/** Maximum account lines return per request to the
|
||||
account_lines command. Specified in the limit param.
|
||||
/** Maximum account objects returned per request from the
|
||||
account_objects command. Specified in the limit param.
|
||||
*/
|
||||
unsigned int const maxLinesPerRequest (400);
|
||||
static unsigned int const maxObjectsPerRequest (400);
|
||||
|
||||
/** Default offers return per request to the account_offers command
|
||||
when no limit param is specified
|
||||
/** Default account lines returned per request from the
|
||||
account_lines command when no limit param is specified
|
||||
*/
|
||||
unsigned int const defaultOffersPerRequest (200);
|
||||
static unsigned int const defaultLinesPerRequest (200);
|
||||
|
||||
/** Minimum offers return per request to the account_offers command.
|
||||
Specified in the limit param.
|
||||
/** Minimum account lines returned per request from the
|
||||
account_lines command. Specified in the limit param.
|
||||
*/
|
||||
unsigned int const minOffersPerRequest (10);
|
||||
static unsigned int const minLinesPerRequest (10);
|
||||
|
||||
/** Maximum offers return per request to the account_lines command.
|
||||
Specified in the limit param.
|
||||
/** Maximum account lines returned per request from the
|
||||
account_lines command. Specified in the limit param.
|
||||
*/
|
||||
unsigned int const maxOffersPerRequest (400);
|
||||
static unsigned int const maxLinesPerRequest (400);
|
||||
|
||||
int const defaultAutoFillFeeMultiplier (10);
|
||||
int const maxPathfindsInProgress (2);
|
||||
int const maxPathfindJobCount (50);
|
||||
int const maxJobQueueClients (500);
|
||||
int const maxValidatedLedgerAge (120);
|
||||
int const maxRequestSize (1000000);
|
||||
/** Default offers returned per request from the
|
||||
account_offers command when no limit param is specified.
|
||||
*/
|
||||
static unsigned int const defaultOffersPerRequest (200);
|
||||
|
||||
/** Minimum offers returned per request from the
|
||||
account_offers command. Specified in the limit param.
|
||||
*/
|
||||
static unsigned int const minOffersPerRequest (10);
|
||||
|
||||
/** Maximum offers returned per request from the
|
||||
account_lines command. Specified in the limit param.
|
||||
*/
|
||||
static unsigned int const maxOffersPerRequest (400);
|
||||
|
||||
static int const defaultAutoFillFeeMultiplier (10);
|
||||
static int const maxPathfindsInProgress (2);
|
||||
static int const maxPathfindJobCount (50);
|
||||
static int const maxJobQueueClients (500);
|
||||
static int const maxValidatedLedgerAge (120);
|
||||
static int const maxRequestSize (1000000);
|
||||
|
||||
} // Tuning
|
||||
/** @} */
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <ripple/rpc/handlers/AccountCurrencies.cpp>
|
||||
#include <ripple/rpc/handlers/AccountInfo.cpp>
|
||||
#include <ripple/rpc/handlers/AccountLines.cpp>
|
||||
#include <ripple/rpc/handlers/AccountObjects.cpp>
|
||||
#include <ripple/rpc/handlers/AccountOffers.cpp>
|
||||
#include <ripple/rpc/handlers/AccountTx.cpp>
|
||||
#include <ripple/rpc/handlers/AccountTxOld.cpp>
|
||||
|
||||
238
test/account_objects-test.js
Normal file
238
test/account_objects-test.js
Normal file
@@ -0,0 +1,238 @@
|
||||
var async = require("async");
|
||||
var assert = require('assert');
|
||||
var http = require("http");
|
||||
var jsonrpc = require("simple-jsonrpc");
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Remote = require("ripple-lib").Remote;
|
||||
var testutils = require("./testutils");
|
||||
var config = testutils.init_config();
|
||||
|
||||
function build_setup(options) {
|
||||
var setup = testutils.build_setup(options);
|
||||
|
||||
return function (done) {
|
||||
var self = this;
|
||||
|
||||
var http_config = config.http_servers["zed"];
|
||||
|
||||
self.server_events = new EventEmitter;
|
||||
|
||||
self.server = http.createServer(function (req, res) {
|
||||
// console.log("REQUEST");
|
||||
var input = "";
|
||||
|
||||
req.setEncoding('utf8');
|
||||
|
||||
req.on('data', function (buffer) {
|
||||
// console.log("DATA: %s", buffer);
|
||||
input = input + buffer;
|
||||
});
|
||||
|
||||
req.on('end', function () {
|
||||
var request = JSON.parse(input);
|
||||
// console.log("REQ: %s", JSON.stringify(request, undefined, 2));
|
||||
self.server_events.emit('request', request, res);
|
||||
});
|
||||
|
||||
req.on('close', function () { });
|
||||
});
|
||||
|
||||
self.server.listen(http_config.port, http_config.ip, void(0), function () {
|
||||
// console.log("server up: %s %d", http_config.ip, http_config.port);
|
||||
setup.call(self, done);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
function build_teardown() {
|
||||
var teardown = testutils.build_teardown();
|
||||
|
||||
return function (done) {
|
||||
var self = this;
|
||||
|
||||
self.server.close(function () {
|
||||
// console.log("server closed");
|
||||
|
||||
teardown.call(self, done);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
suite('ACCOUNT_OBJECTS', function() {
|
||||
var $ = { };
|
||||
|
||||
setup(function(done) {
|
||||
build_setup().call($, done);
|
||||
});
|
||||
|
||||
teardown(function(done) {
|
||||
build_teardown().call($, done);
|
||||
});
|
||||
|
||||
test('account_objects', function(done) {
|
||||
var self = this;
|
||||
|
||||
var rippled_config = testutils.get_server_config(config);
|
||||
var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" +
|
||||
rippled_config.rpc_port);
|
||||
var http_config = config.http_servers["zed"];
|
||||
|
||||
var steps = [
|
||||
function (callback) {
|
||||
self.what = 'Create accounts';
|
||||
|
||||
testutils.create_accounts(
|
||||
$.remote,
|
||||
'root',
|
||||
'20000.0',
|
||||
[ 'mtgox', 'alice', 'bob' ],
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
function waitLedgers(callback) {
|
||||
self.what = 'Wait ledger';
|
||||
|
||||
$.remote.once('ledger_closed', function() {
|
||||
callback();
|
||||
});
|
||||
|
||||
$.remote.ledger_accept();
|
||||
},
|
||||
|
||||
function verifyBalance(callback) {
|
||||
self.what = 'Verify balance';
|
||||
|
||||
testutils.verify_balance(
|
||||
$.remote,
|
||||
[ 'mtgox', 'alice', 'bob' ],
|
||||
'19999999988',
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
function (callback) {
|
||||
self.what = 'Set transfer rate';
|
||||
|
||||
var tx = $.remote.transaction('AccountSet', {
|
||||
account: 'mtgox'
|
||||
});
|
||||
|
||||
tx.transferRate(1.1 * 1e9);
|
||||
|
||||
tx.submit(function(err, m) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(m.engine_result, 'tesSUCCESS');
|
||||
callback();
|
||||
});
|
||||
|
||||
testutils.ledger_wait($.remote, tx);
|
||||
},
|
||||
|
||||
function (callback) {
|
||||
self.what = 'Set limits';
|
||||
|
||||
testutils.credit_limits($.remote, {
|
||||
'alice' : '1000/USD/mtgox',
|
||||
'bob' : '1000/USD/mtgox'
|
||||
},
|
||||
callback);
|
||||
},
|
||||
|
||||
function (callback) {
|
||||
self.what = 'Distribute funds';
|
||||
|
||||
testutils.payments($.remote, {
|
||||
'mtgox' : [ '100/USD/alice', '50/USD/bob' ]
|
||||
},
|
||||
callback);
|
||||
},
|
||||
|
||||
function (callback) {
|
||||
self.what = 'Create offer';
|
||||
|
||||
// get 4000/XRP pay 10/USD : offer pays 10 USD for 4000 XRP
|
||||
var tx = $.remote.transaction('OfferCreate', {
|
||||
account: 'alice',
|
||||
taker_pays: '4000',
|
||||
taker_gets: '10/USD/mtgox'
|
||||
});
|
||||
|
||||
tx.submit(function(err, m) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(m.engine_result, 'tesSUCCESS');
|
||||
callback();
|
||||
});
|
||||
|
||||
testutils.ledger_wait($.remote, tx);
|
||||
},
|
||||
|
||||
function (callback) {
|
||||
self.what = "Get account objects.";
|
||||
|
||||
client.call('account_objects', [{
|
||||
"account": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"limit": 10
|
||||
}], function (result) {
|
||||
// console.log(JSON.stringify(result, undefined, 2));
|
||||
assert(typeof result === 'object');
|
||||
|
||||
assert('account' in result);
|
||||
assert.deepEqual(result['account'], 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn');
|
||||
|
||||
assert('account_objects' in result);
|
||||
var expected = [{
|
||||
Balance: {
|
||||
currency: 'USD',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
value: '-100'
|
||||
},
|
||||
Flags: 131072,
|
||||
HighLimit: {
|
||||
currency: 'USD',
|
||||
issuer: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
|
||||
value: '1000'
|
||||
},
|
||||
HighNode: '0000000000000000',
|
||||
LedgerEntryType: 'RippleState',
|
||||
LowLimit: {
|
||||
currency: 'USD',
|
||||
issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v',
|
||||
value: '0'
|
||||
},
|
||||
LowNode: '0000000000000000',
|
||||
PreviousTxnID: result['account_objects'][0]['PreviousTxnID'],
|
||||
PreviousTxnLgrSeq: result['account_objects'][0]['PreviousTxnLgrSeq'],
|
||||
index: 'DE9CF5B006C8EA021CAB2ED20F01FC9D3260875C885155E7FA7A4DB534E36D8A'
|
||||
}, {
|
||||
Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
|
||||
BookDirectory:
|
||||
'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000000',
|
||||
PreviousTxnID: result['account_objects'][1]['PreviousTxnID'],
|
||||
PreviousTxnLgrSeq: result['account_objects'][1]['PreviousTxnLgrSeq'],
|
||||
Sequence: 3,
|
||||
TakerGets: {
|
||||
currency: 'USD',
|
||||
issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v',
|
||||
value: '10'
|
||||
},
|
||||
TakerPays: '4000',
|
||||
index: '2A432F386EF28151AF60885CE201CC9331FF494A163D40531A9D253C97E81D61'
|
||||
}];
|
||||
|
||||
assert.deepEqual(result['account_objects'], expected);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
async.waterfall(steps, function(error) {
|
||||
assert(!error, self.what + ': ' + error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user