mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Port test/transaction_ordering_test.js to C++
This commit is contained in:
@@ -1736,6 +1736,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tests\Transaction_ordering_test.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tests\TxQ_test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tests\TxQ_test.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -2460,6 +2460,9 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\app\tests\Taker.test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tests\Taker.test.cpp">
|
||||||
<Filter>ripple\app\tests</Filter>
|
<Filter>ripple\app\tests</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tests\Transaction_ordering_test.cpp">
|
||||||
|
<Filter>ripple\app\tests</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tests\TxQ_test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tests\TxQ_test.cpp">
|
||||||
<Filter>ripple\app\tests</Filter>
|
<Filter>ripple\app\tests</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
179
src/ripple/app/tests/Transaction_ordering_test.cpp
Normal file
179
src/ripple/app/tests/Transaction_ordering_test.cpp
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/ErrorCodes.h>
|
||||||
|
#include <ripple/test/jtx.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
struct Transaction_ordering_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
template <class Ftest>
|
||||||
|
void submitWait(jtx::Env& env, jtx::JTx const& tx, Ftest&& test)
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
std::condition_variable cv;
|
||||||
|
env.app().getJobQueue().postCoro(
|
||||||
|
jtCLIENT, "Coroutine-Test",
|
||||||
|
[&](std::shared_ptr<JobCoro> jc)
|
||||||
|
{
|
||||||
|
env(tx);
|
||||||
|
cv.notify_one();
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
std::mutex m;
|
||||||
|
std::unique_lock<std::mutex> lk(m);
|
||||||
|
// If stepping through this test in a debugger,
|
||||||
|
// make the timeout much longer, or use
|
||||||
|
//cv.wait(lk, test);
|
||||||
|
expect(cv.wait_for(lk, 2s, test));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCorrectOrder()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
Env env(*this);
|
||||||
|
auto const alice = Account("alice");
|
||||||
|
env.fund(XRP(1000), noripple(alice));
|
||||||
|
|
||||||
|
auto const aliceSequence = env.seq(alice);
|
||||||
|
|
||||||
|
auto const tx1 = env.jt(noop(alice), seq(aliceSequence));
|
||||||
|
auto const tx2 = env.jt(noop(alice), seq(aliceSequence + 1),
|
||||||
|
json(R"({"LastLedgerSequence":7})"));
|
||||||
|
|
||||||
|
env(tx1);
|
||||||
|
env.close();
|
||||||
|
expect(env.seq(alice) == aliceSequence + 1);
|
||||||
|
env(tx2);
|
||||||
|
env.close();
|
||||||
|
expect(env.seq(alice) == aliceSequence + 2);
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
{
|
||||||
|
auto const result = env.rpc("tx", to_string(tx1.stx->getTransactionID()));
|
||||||
|
expect(result.first == rpcSUCCESS);
|
||||||
|
expect(result.second["result"]["meta"]["TransactionResult"] == "tesSUCCESS");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto const result = env.rpc("tx", to_string(tx2.stx->getTransactionID()));
|
||||||
|
expect(result.first == rpcSUCCESS);
|
||||||
|
expect(result.second["result"]["meta"]["TransactionResult"] == "tesSUCCESS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testIncorrectOrder()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
Env env(*this);
|
||||||
|
env.app().getJobQueue().setThreadCount(0, false);
|
||||||
|
auto const alice = Account("alice");
|
||||||
|
env.fund(XRP(1000), noripple(alice));
|
||||||
|
|
||||||
|
auto const aliceSequence = env.seq(alice);
|
||||||
|
|
||||||
|
auto const tx1 = env.jt(noop(alice), seq(aliceSequence));
|
||||||
|
auto const tx2 = env.jt(noop(alice), seq(aliceSequence + 1),
|
||||||
|
json(R"({"LastLedgerSequence":7})"));
|
||||||
|
|
||||||
|
env(tx2, ter(terPRE_SEQ));
|
||||||
|
expect(env.seq(alice) == aliceSequence);
|
||||||
|
submitWait(env, tx1,
|
||||||
|
[&]()
|
||||||
|
{
|
||||||
|
return env.seq(alice) == aliceSequence + 2;
|
||||||
|
});
|
||||||
|
expect(env.seq(alice) == aliceSequence + 2);
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
{
|
||||||
|
auto const result = env.rpc("tx", to_string(tx1.stx->getTransactionID()));
|
||||||
|
expect(result.first == rpcSUCCESS);
|
||||||
|
expect(result.second["result"]["meta"]["TransactionResult"] == "tesSUCCESS");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto const result = env.rpc("tx", to_string(tx2.stx->getTransactionID()));
|
||||||
|
expect(result.first == rpcSUCCESS);
|
||||||
|
expect(result.second["result"]["meta"]["TransactionResult"] == "tesSUCCESS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testIncorrectOrderMultipleIntermediaries()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
Env env(*this);
|
||||||
|
env.app().getJobQueue().setThreadCount(0, false);
|
||||||
|
auto const alice = Account("alice");
|
||||||
|
env.fund(XRP(1000), noripple(alice));
|
||||||
|
|
||||||
|
auto const aliceSequence = env.seq(alice);
|
||||||
|
|
||||||
|
std::vector<JTx> tx;
|
||||||
|
for (auto i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
tx.emplace_back(
|
||||||
|
env.jt(noop(alice), seq(aliceSequence + i),
|
||||||
|
json(R"({"LastLedgerSequence":7})"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 1; i < 5; ++i)
|
||||||
|
{
|
||||||
|
env(tx[i], ter(terPRE_SEQ));
|
||||||
|
expect(env.seq(alice) == aliceSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
submitWait(env, tx[0],
|
||||||
|
[&]()
|
||||||
|
{
|
||||||
|
return env.seq(alice) == aliceSequence + 5;
|
||||||
|
});
|
||||||
|
expect(env.seq(alice) == aliceSequence + 5);
|
||||||
|
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
for (auto i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
auto const result = env.rpc("tx", to_string(tx[i].stx->getTransactionID()));
|
||||||
|
expect(result.first == rpcSUCCESS);
|
||||||
|
expect(result.second["result"]["meta"]["TransactionResult"] == "tesSUCCESS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
testCorrectOrder();
|
||||||
|
testIncorrectOrder();
|
||||||
|
testIncorrectOrderMultipleIntermediaries();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(Transaction_ordering,app,ripple);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // ripple
|
||||||
@@ -33,5 +33,6 @@
|
|||||||
#include <ripple/app/tests/SetAuth_test.cpp>
|
#include <ripple/app/tests/SetAuth_test.cpp>
|
||||||
#include <ripple/app/tests/OversizeMeta_test.cpp>
|
#include <ripple/app/tests/OversizeMeta_test.cpp>
|
||||||
#include <ripple/app/tests/Taker.test.cpp>
|
#include <ripple/app/tests/Taker.test.cpp>
|
||||||
|
#include <ripple/app/tests/Transaction_ordering_test.cpp>
|
||||||
#include <ripple/app/tests/TxQ_test.cpp>
|
#include <ripple/app/tests/TxQ_test.cpp>
|
||||||
#include <ripple/app/tests/ValidatorList_test.cpp>
|
#include <ripple/app/tests/ValidatorList_test.cpp>
|
||||||
|
|||||||
@@ -1,255 +0,0 @@
|
|||||||
let assert = require('assert');
|
|
||||||
let _ = require('lodash');
|
|
||||||
let async = require('async');
|
|
||||||
let testutils = require('./testutils');
|
|
||||||
let config = testutils.init_config();
|
|
||||||
let accounts = require('./testconfig').accounts;
|
|
||||||
let Amount = require('ripple-lib').Amount;
|
|
||||||
let Transaction = require('ripple-lib').Transaction;
|
|
||||||
|
|
||||||
suite('Transaction Ordering', function() {
|
|
||||||
let $ = {};
|
|
||||||
let opts = {};
|
|
||||||
|
|
||||||
setup(function(done) {
|
|
||||||
testutils.build_setup(opts).call($, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
setup(function(done) {
|
|
||||||
$.remote.local_signing = true;
|
|
||||||
testutils.create_accounts(
|
|
||||||
$.remote,
|
|
||||||
'root',
|
|
||||||
Amount.from_human('1000 XRP'),
|
|
||||||
['alice'],
|
|
||||||
done);
|
|
||||||
});
|
|
||||||
|
|
||||||
teardown(function(done) {
|
|
||||||
testutils.build_teardown().call($, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
function getAliceSequence() {
|
|
||||||
return $.remote.account('alice')._entry.Sequence + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirmAliceSeq(expected, callback) {
|
|
||||||
let request = $.remote.requestAccountInfo(
|
|
||||||
{ account: accounts.alice.account, ledger: 'current' },
|
|
||||||
function(err, res) {
|
|
||||||
assert(!err);
|
|
||||||
// console.log("Alice seq: ", res.account_data.Sequence, " Want: ", expected);
|
|
||||||
assert.strictEqual(res.account_data.Sequence, expected);
|
|
||||||
|
|
||||||
if(callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
test('correct order', function(done) {
|
|
||||||
let aliceSequence = getAliceSequence();
|
|
||||||
confirmAliceSeq(aliceSequence);
|
|
||||||
|
|
||||||
let tx1 = $.remote.createTransaction('AccountSet',
|
|
||||||
{account: accounts.alice.account});
|
|
||||||
tx1.setSequence(aliceSequence);
|
|
||||||
|
|
||||||
let tx2 = $.remote.createTransaction('AccountSet',
|
|
||||||
{account: accounts.alice.account});
|
|
||||||
tx2.setSequence(aliceSequence + 1);
|
|
||||||
tx2.setLastLedgerSequenceOffset(5);
|
|
||||||
|
|
||||||
async.series([
|
|
||||||
function(callback) {
|
|
||||||
tx1.once('submitted', function(res) {
|
|
||||||
// console.log("tx1 submitted: ", res.engine_result);
|
|
||||||
assert.strictEqual(res.engine_result, 'tesSUCCESS');
|
|
||||||
|
|
||||||
$.remote.ledger_accept();
|
|
||||||
});
|
|
||||||
tx1.once('final', function(res) {
|
|
||||||
// console.log("tx1 final: ", res.metadata.TransactionResult);
|
|
||||||
assert.strictEqual(res.metadata.TransactionResult,
|
|
||||||
'tesSUCCESS');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
tx1.submit();
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
confirmAliceSeq(aliceSequence + 1, callback);
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
tx2.once('submitted', function(res) {
|
|
||||||
// console.log("tx2 submitted: ", res.engine_result);
|
|
||||||
assert.strictEqual(res.engine_result, 'tesSUCCESS');
|
|
||||||
|
|
||||||
$.remote.ledger_accept();
|
|
||||||
});
|
|
||||||
tx2.once('final', function(res) {
|
|
||||||
// console.log("tx2 final: ", res.metadata.TransactionResult);
|
|
||||||
assert.strictEqual(res.metadata.TransactionResult,
|
|
||||||
'tesSUCCESS');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
tx2.submit();
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
confirmAliceSeq(aliceSequence + 2, callback);
|
|
||||||
}
|
|
||||||
], done);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('incorrect order', function(done) {
|
|
||||||
let aliceSequence = getAliceSequence();
|
|
||||||
confirmAliceSeq(aliceSequence);
|
|
||||||
|
|
||||||
let tx1 = $.remote.createTransaction('AccountSet',
|
|
||||||
{account: accounts.alice.account});
|
|
||||||
tx1.setSequence(aliceSequence);
|
|
||||||
|
|
||||||
let tx2 = $.remote.createTransaction('AccountSet',
|
|
||||||
{account: accounts.alice.account});
|
|
||||||
tx2.setSequence(aliceSequence + 1);
|
|
||||||
tx2.setLastLedgerSequenceOffset(5);
|
|
||||||
|
|
||||||
async.series([
|
|
||||||
function(callback) {
|
|
||||||
// Use `on` instead of `once` for this event, because
|
|
||||||
// we want it to fail if ripple-lib resubmits on our
|
|
||||||
// behalf, especially if it succeeds.
|
|
||||||
tx2.on('submitted', function(res) {
|
|
||||||
// console.log("tx2 submitted: ", res.engine_result);
|
|
||||||
assert.strictEqual(res.engine_result, 'terPRE_SEQ');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
tx2.submit();
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
confirmAliceSeq(aliceSequence, callback);
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
tx1.once('submitted', function(res) {
|
|
||||||
// console.log("tx1 submitted: ", res.engine_result);
|
|
||||||
assert.strictEqual(res.engine_result, 'tesSUCCESS');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
tx1.submit();
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
confirmAliceSeq(aliceSequence + 2, callback);
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
tx1.once('final', function(res) {
|
|
||||||
// console.log("tx1 final: ", res.metadata.TransactionResult);
|
|
||||||
assert.strictEqual(res.metadata.TransactionResult,
|
|
||||||
'tesSUCCESS');
|
|
||||||
});
|
|
||||||
tx2.once('final', function(res) {
|
|
||||||
// console.log("tx2 final: ", res.metadata.TransactionResult);
|
|
||||||
assert.strictEqual(res.metadata.TransactionResult,
|
|
||||||
'tesSUCCESS');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
$.remote.ledger_accept();
|
|
||||||
}
|
|
||||||
], done);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('incorrect order, multiple intermediaries', function(done) {
|
|
||||||
let aliceSequence = getAliceSequence();
|
|
||||||
confirmAliceSeq(aliceSequence);
|
|
||||||
|
|
||||||
let tx = [];
|
|
||||||
for(let i = 0; i < 5; ++i) {
|
|
||||||
tx[i] = $.remote.createTransaction('AccountSet',
|
|
||||||
{account: accounts.alice.account});
|
|
||||||
tx[i].setSequence(aliceSequence + i);
|
|
||||||
tx[i].setLastLedgerSequenceOffset(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
let submits = [];
|
|
||||||
for(let i = 0; i < 3; ++i) {
|
|
||||||
submits = submits.concat([
|
|
||||||
function(callback) {
|
|
||||||
tx[i].once('submitted', function(res) {
|
|
||||||
// console.log("tx" + i + " submitted: ", res.engine_result);
|
|
||||||
assert.strictEqual(res.engine_result, 'tesSUCCESS');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
tx[i].submit();
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
confirmAliceSeq(aliceSequence + i + 1, callback);
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async.series([
|
|
||||||
function(callback) {
|
|
||||||
// Use `on` instead of `once` for this event, because
|
|
||||||
// we want it to fail if ripple-lib resubmits on our
|
|
||||||
// behalf, especially if it succeeds.
|
|
||||||
tx[4].on('submitted', function(res) {
|
|
||||||
// console.log("tx4 submitted: ", res.engine_result);
|
|
||||||
assert.strictEqual(res.engine_result, 'terPRE_SEQ');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
tx[4].submit();
|
|
||||||
// Note ripple-lib has a bug/feature that it'll create
|
|
||||||
// transactions to fill in sequence gaps if more than
|
|
||||||
// one "ter" is received, so this is the best we can do.
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
confirmAliceSeq(aliceSequence, callback);
|
|
||||||
},
|
|
||||||
].concat(submits).concat([
|
|
||||||
function(callback) {
|
|
||||||
tx[3].once('submitted', function(res) {
|
|
||||||
// console.log("tx3 submitted: ", res.engine_result);
|
|
||||||
assert.strictEqual(res.engine_result, 'tesSUCCESS');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
tx[3].submit();
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
confirmAliceSeq(aliceSequence + 5, callback);
|
|
||||||
},
|
|
||||||
function(callback) {
|
|
||||||
for(let i = 0; i < 4; ++i) {
|
|
||||||
tx[i].once('final', function(res) {
|
|
||||||
// console.log("tx" + i + " final: ", res.metadata.TransactionResult);
|
|
||||||
assert.strictEqual(res.metadata.TransactionResult,
|
|
||||||
'tesSUCCESS');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tx[4].once('final', function(res) {
|
|
||||||
// console.log("tx4 final: ", res.metadata.TransactionResult);
|
|
||||||
assert.strictEqual(res.metadata.TransactionResult,
|
|
||||||
'tesSUCCESS');
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
|
|
||||||
$.remote.ledger_accept();
|
|
||||||
}
|
|
||||||
]), done);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user