mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
RPC robust transaction unit test (RIPD-1079)
This commit is contained in:
committed by
Vinnie Falco
parent
1d0ca51c88
commit
8f83f69325
321
src/ripple/rpc/tests/RobustTransaction.test.cpp
Normal file
321
src/ripple/rpc/tests/RobustTransaction.test.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 <BeastConfig.h>
|
||||
#include <ripple/core/JobQueue.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/test/WSClient.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class RobustTransaction_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testSequenceRealignment()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.close();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
// RPC subscribe to transactions stream
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = Json::arrayValue;
|
||||
jv[jss::streams].append("transactions");
|
||||
jv = wsc->invoke("subscribe", jv);
|
||||
expect(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// Submit past ledger sequence transaction
|
||||
Json::Value payment;
|
||||
payment[jss::secret] = toBase58(generateSeed("alice"));
|
||||
payment[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
payment[jss::tx_json][sfLastLedgerSequence.fieldName] = 1;
|
||||
auto jv = wsc->invoke("submit", payment);
|
||||
expect(jv[jss::result][jss::engine_result] ==
|
||||
"tefMAX_LEDGER");
|
||||
|
||||
// Submit past sequence transaction
|
||||
payment[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
payment[jss::tx_json][sfSequence.fieldName] =
|
||||
env.seq("alice") - 1;
|
||||
jv = wsc->invoke("submit", payment);
|
||||
expect(jv[jss::result][jss::engine_result] ==
|
||||
"tefPAST_SEQ");
|
||||
|
||||
// Submit future sequence transaction
|
||||
payment[jss::tx_json][sfSequence.fieldName] =
|
||||
env.seq("alice") + 1;
|
||||
jv = wsc->invoke("submit", payment);
|
||||
expect(jv[jss::result][jss::engine_result] ==
|
||||
"terPRE_SEQ");
|
||||
|
||||
// Submit transaction to bridge the sequence gap
|
||||
payment[jss::tx_json][sfSequence.fieldName] =
|
||||
env.seq("alice");
|
||||
jv = wsc->invoke("submit", payment);
|
||||
expect(jv[jss::result][jss::engine_result] ==
|
||||
"tesSUCCESS");
|
||||
|
||||
// Wait for the jobqueue to process everything
|
||||
env.app().getJobQueue().rendezvous();
|
||||
|
||||
// Finalize transactions
|
||||
jv = wsc->invoke("ledger_accept");
|
||||
expect(jv[jss::result].isMember(
|
||||
jss::ledger_current_index));
|
||||
}
|
||||
|
||||
{
|
||||
// Check balances
|
||||
expect(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& ff = jv[jss::meta]["AffectedNodes"]
|
||||
[1u]["ModifiedNode"]["FinalFields"];
|
||||
return ff[jss::Account] == Account("bob").human() &&
|
||||
ff["Balance"] == "10001000000";
|
||||
}));
|
||||
|
||||
expect(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& ff = jv[jss::meta]["AffectedNodes"]
|
||||
[1u]["ModifiedNode"]["FinalFields"];
|
||||
return ff[jss::Account] == Account("bob").human() &&
|
||||
ff["Balance"] == "10002000000";
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Submit a normal payment. Client disconnects after the proposed
|
||||
transaction result is received.
|
||||
|
||||
Client reconnects in the future. During this time it is presumed that the
|
||||
transaction should have succeeded.
|
||||
|
||||
Upon reconnection, recent account transaction history is loaded.
|
||||
The submitted transaction should be detected, and the transaction should
|
||||
ultimately succeed.
|
||||
*/
|
||||
void
|
||||
testReconnect()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.close();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
// Submit normal payment
|
||||
Json::Value jv;
|
||||
jv[jss::secret] = toBase58(generateSeed("alice"));
|
||||
jv[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
jv = wsc->invoke("submit", jv);
|
||||
expect(jv[jss::result][jss::engine_result] ==
|
||||
"tesSUCCESS");
|
||||
|
||||
// Disconnect
|
||||
wsc.reset();
|
||||
|
||||
// Server finalizes transaction
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// RPC account_tx
|
||||
Json::Value jv;
|
||||
jv[jss::account] = Account("bob").human();
|
||||
jv[jss::ledger_index_min] = -1;
|
||||
jv[jss::ledger_index_max] = -1;
|
||||
wsc = makeWSClient(env.app().config());
|
||||
jv = wsc->invoke("account_tx", jv);
|
||||
|
||||
// Check balance
|
||||
auto ff = jv[jss::result][jss::transactions][0u][jss::meta]
|
||||
["AffectedNodes"][1u]["ModifiedNode"]["FinalFields"];
|
||||
expect(ff[jss::Account] ==
|
||||
Account("bob").human());
|
||||
expect(ff["Balance"] == "10001000000");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testReconnectAfterWait()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.close();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
// Submit normal payment
|
||||
Json::Value jv;
|
||||
jv[jss::secret] = toBase58(generateSeed("alice"));
|
||||
jv[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
jv = wsc->invoke("submit", jv);
|
||||
expect(jv[jss::result][jss::engine_result] ==
|
||||
"tesSUCCESS");
|
||||
|
||||
// Finalize transaction
|
||||
jv = wsc->invoke("ledger_accept");
|
||||
expect(jv[jss::result].isMember(
|
||||
jss::ledger_current_index));
|
||||
|
||||
// Wait for the jobqueue to process everything
|
||||
env.app().getJobQueue().rendezvous();
|
||||
}
|
||||
|
||||
{
|
||||
// RPC subscribe to ledger stream
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = Json::arrayValue;
|
||||
jv[jss::streams].append("ledger");
|
||||
jv = wsc->invoke("subscribe", jv);
|
||||
expect(jv[jss::status] == "success");
|
||||
|
||||
// Close ledgers
|
||||
for(auto i = 0; i < 8; ++i)
|
||||
{
|
||||
expect(wsc->invoke("ledger_accept")[jss::result].
|
||||
isMember(jss::ledger_current_index));
|
||||
|
||||
// Wait for the jobqueue to process everything
|
||||
env.app().getJobQueue().rendezvous();
|
||||
|
||||
expect(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::type] == "ledgerClosed";
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Disconnect, reconnect
|
||||
wsc = makeWSClient(env.app().config());
|
||||
|
||||
// RPC subscribe to ledger stream
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = Json::arrayValue;
|
||||
jv[jss::streams].append("ledger");
|
||||
jv = wsc->invoke("subscribe", jv);
|
||||
expect(jv[jss::status] == "success");
|
||||
|
||||
// Close ledgers
|
||||
for (auto i = 0; i < 2; ++i)
|
||||
{
|
||||
expect(wsc->invoke("ledger_accept")[jss::result].
|
||||
isMember(jss::ledger_current_index));
|
||||
|
||||
// Wait for the jobqueue to process everything
|
||||
env.app().getJobQueue().rendezvous();
|
||||
|
||||
expect(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::type] == "ledgerClosed";
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// RPC account_tx
|
||||
Json::Value jv;
|
||||
jv[jss::account] = Account("bob").human();
|
||||
jv[jss::ledger_index_min] = -1;
|
||||
jv[jss::ledger_index_max] = -1;
|
||||
wsc = makeWSClient(env.app().config());
|
||||
jv = wsc->invoke("account_tx", jv);
|
||||
|
||||
// Check balance
|
||||
auto ff = jv[jss::result][jss::transactions][0u][jss::meta]
|
||||
["AffectedNodes"][1u]["ModifiedNode"]["FinalFields"];
|
||||
expect(ff[jss::Account] ==
|
||||
Account("bob").human());
|
||||
expect(ff["Balance"] == "10001000000");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testAccountsProposed()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
env.close();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
// RPC subscribe to accounts_proposed stream
|
||||
Json::Value jv;
|
||||
jv[jss::accounts_proposed] = Json::arrayValue;
|
||||
jv[jss::accounts_proposed].append(
|
||||
Account("alice").human());
|
||||
jv = wsc->invoke("subscribe", jv);
|
||||
expect(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// Submit account_set transaction
|
||||
Json::Value jv;
|
||||
jv[jss::secret] = toBase58(generateSeed("alice"));
|
||||
jv[jss::tx_json] = fset("alice", 0);
|
||||
jv[jss::tx_json][jss::Fee] = 10;
|
||||
jv = wsc->invoke("submit", jv);
|
||||
expect(jv[jss::result][jss::engine_result] ==
|
||||
"tesSUCCESS");
|
||||
}
|
||||
|
||||
{
|
||||
// Check stream update
|
||||
expect(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::transaction][jss::TransactionType] ==
|
||||
"AccountSet";
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testSequenceRealignment();
|
||||
testReconnect();
|
||||
testReconnectAfterWait();
|
||||
testAccountsProposed();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(RobustTransaction,app,ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
@@ -105,5 +105,6 @@
|
||||
#include <ripple/rpc/tests/JSONRPC.test.cpp>
|
||||
#include <ripple/rpc/tests/LedgerRequestRPC.test.cpp>
|
||||
#include <ripple/rpc/tests/KeyGeneration.test.cpp>
|
||||
#include <ripple/rpc/tests/RobustTransaction.test.cpp>
|
||||
#include <ripple/rpc/tests/Status.test.cpp>
|
||||
#include <ripple/rpc/tests/Subscribe.test.cpp>
|
||||
|
||||
Reference in New Issue
Block a user