diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 96a509995..b6dc1bc0c 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -4899,6 +4899,10 @@
True
True
+
+ True
+ True
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index 695674530..3113a973c 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -5574,6 +5574,9 @@
test\rpc
+
+ test\rpc
+
test\server
diff --git a/src/ripple/rpc/handlers/TransactionEntry.cpp b/src/ripple/rpc/handlers/TransactionEntry.cpp
index 13ea07e20..8987ab096 100644
--- a/src/ripple/rpc/handlers/TransactionEntry.cpp
+++ b/src/ripple/rpc/handlers/TransactionEntry.cpp
@@ -35,18 +35,17 @@ namespace ripple {
// means any ledger.
Json::Value doTransactionEntry (RPC::Context& context)
{
- std::shared_ptr lpLedger;
+ std::shared_ptr lpLedger;
Json::Value jvResult = RPC::lookupLedger (lpLedger, context);
- if (!lpLedger)
+ if(! lpLedger)
return jvResult;
- if (!context.params.isMember (jss::tx_hash))
+ if(! context.params.isMember (jss::tx_hash))
{
- jvResult[jss::error] = "fieldNotFoundTransaction";
+ jvResult[jss::error] = "fieldNotFoundTransaction";
}
- else if (!context.params.isMember (jss::ledger_hash)
- && !context.params.isMember (jss::ledger_index))
+ else if(jvResult.get(jss::ledger_hash, Json::nullValue).isNull())
{
// We don't work on ledger current.
@@ -60,28 +59,19 @@ Json::Value doTransactionEntry (RPC::Context& context)
// routine, returning success or failure.
uTransID.SetHex (context.params[jss::tx_hash].asString ());
- if (!lpLedger)
+ auto tx = lpLedger->txRead (uTransID);
+ if(! tx.first)
{
- jvResult[jss::error] = "ledgerNotFound";
+ jvResult[jss::error] = "transactionNotFound";
}
else
{
- TxMeta::pointer tmTrans;
-
- auto tx = lpLedger->txRead (uTransID);
- if (!tx.first)
- {
- jvResult[jss::error] = "transactionNotFound";
- }
- else
- {
- jvResult[jss::tx_json] = tx.first->getJson (0);
- if (tx.second)
- jvResult[jss::metadata] = tx.second->getJson (0);
- // 'accounts'
- // 'engine_...'
- // 'ledger_...'
- }
+ jvResult[jss::tx_json] = tx.first->getJson (0);
+ if (tx.second)
+ jvResult[jss::metadata] = tx.second->getJson (0);
+ // 'accounts'
+ // 'engine_...'
+ // 'ledger_...'
}
}
diff --git a/src/test/rpc/TransactionEntry_test.cpp b/src/test/rpc/TransactionEntry_test.cpp
new file mode 100644
index 000000000..0f3d49176
--- /dev/null
+++ b/src/test/rpc/TransactionEntry_test.cpp
@@ -0,0 +1,165 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2017 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
+#include
+#include
+
+namespace ripple {
+
+class TransactionEntry_test : public beast::unit_test::suite
+{
+ void
+ testBadInput()
+ {
+ testcase("Invalid request params");
+ using namespace test::jtx;
+ Env env {*this};
+
+ {
+ //no params
+ auto const result = env.client()
+ .invoke("transaction_entry", {})[jss::result];
+ BEAST_EXPECT(result[jss::error] == "fieldNotFoundTransaction");
+ BEAST_EXPECT(result[jss::status] == "error");
+ }
+
+ {
+ Json::Value params {Json::objectValue};
+ params[jss::ledger] = 20;
+ auto const result = env.client()
+ .invoke("transaction_entry", params)[jss::result];
+ BEAST_EXPECT(result[jss::error] == "lgrNotFound");
+ BEAST_EXPECT(result[jss::status] == "error");
+ }
+
+ {
+ Json::Value params {Json::objectValue};
+ params[jss::ledger] = "current";
+ params[jss::tx_hash] = "DEADBEEF";
+ auto const result = env.client()
+ .invoke("transaction_entry", params)[jss::result];
+ BEAST_EXPECT(result[jss::error] == "notYetImplemented");
+ BEAST_EXPECT(result[jss::status] == "error");
+ }
+
+ {
+ Json::Value params {Json::objectValue};
+ params[jss::ledger] = "closed";
+ params[jss::tx_hash] = "DEADBEEF";
+ auto const result = env.client()
+ .invoke("transaction_entry", params)[jss::result];
+ BEAST_EXPECT(! result[jss::ledger_hash].asString().empty());
+ BEAST_EXPECT(result[jss::error] == "transactionNotFound");
+ BEAST_EXPECT(result[jss::status] == "error");
+ }
+ }
+
+ void testRequest()
+ {
+ testcase("Basic request");
+ using namespace test::jtx;
+ Env env {*this};
+
+ auto check_tx = [this, &env]
+ (int index, std::string txhash, std::string type = "")
+ {
+ Json::Value resIndex, resHash;
+ // first request using ledger_index to lookup
+ {
+ Json::Value params {Json::objectValue};
+ params[jss::ledger_index] = index;
+ params[jss::tx_hash] = txhash;
+ resIndex = env.client()
+ .invoke("transaction_entry", params)[jss::result];
+ if(! BEAST_EXPECTS(resIndex.isMember(jss::tx_json), txhash))
+ return;
+ BEAST_EXPECT(resIndex[jss::tx_json][jss::hash] == txhash);
+ if(! type.empty())
+ BEAST_EXPECTS(
+ resIndex[jss::tx_json][jss::TransactionType] == type,
+ txhash + " is " +
+ resIndex[jss::tx_json][jss::TransactionType].asString());
+ }
+
+ // second request using ledger_hash to lookup and verify
+ // both responses match
+ {
+ Json::Value params {Json::objectValue};
+ params[jss::ledger_hash] = resIndex[jss::ledger_hash];
+ params[jss::tx_hash] = txhash;
+ resHash = env.client()
+ .invoke("transaction_entry", params)[jss::result];
+ BEAST_EXPECT(resHash == resIndex);
+ }
+ };
+
+ Account A1 {"A1"};
+ Account A2 {"A2"};
+
+ env.fund(XRP(10000), A1);
+ auto fund_1_tx =
+ boost::lexical_cast(env.tx()->getTransactionID());
+
+ env.fund(XRP(10000), A2);
+ auto fund_2_tx =
+ boost::lexical_cast(env.tx()->getTransactionID());
+
+ env.close();
+
+ // these are actually AccountSet txs because fund does two txs and
+ // env.tx only reports the last one
+ check_tx(env.closed()->seq(), fund_1_tx);
+ check_tx(env.closed()->seq(), fund_2_tx);
+
+ env.trust(A2["USD"](1000), A1);
+ // the trust tx is actually a payment since the trust method
+ // refunds fees with a payment after TrustSet..so just ignore the type
+ // in the check below
+ auto trust_tx =
+ boost::lexical_cast(env.tx()->getTransactionID());
+
+ env(pay(A2, A1, A2["USD"](5)));
+ auto pay_tx =
+ boost::lexical_cast(env.tx()->getTransactionID());
+ env.close();
+
+ check_tx(env.closed()->seq(), trust_tx);
+ check_tx(env.closed()->seq(), pay_tx, "Payment");
+
+ env(offer(A2, XRP(100), A2["USD"](1)));
+ auto offer_tx =
+ boost::lexical_cast(env.tx()->getTransactionID());
+
+ env.close();
+
+ check_tx(env.closed()->seq(), offer_tx, "OfferCreate");
+ }
+
+public:
+ void run ()
+ {
+ testBadInput();
+ testRequest();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE (TransactionEntry, rpc, ripple);
+
+} // ripple
diff --git a/src/test/unity/rpc_test_unity.cpp b/src/test/unity/rpc_test_unity.cpp
index 17c9c61d2..2ab89c9d1 100644
--- a/src/test/unity/rpc_test_unity.cpp
+++ b/src/test/unity/rpc_test_unity.cpp
@@ -38,3 +38,4 @@
#include
#include
#include
+#include