rippled
Submit_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2020 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/basics/StringUtilities.h>
21 #include <ripple/protocol/Feature.h>
22 #include <ripple/protocol/jss.h>
23 #include <test/jtx.h>
24 #include <test/jtx/WSClient.h>
25 
26 #include <ripple/resource/Charge.h>
27 #include <ripple/resource/Fees.h>
28 #include <ripple/rpc/GRPCHandlers.h>
29 #include <test/rpc/GRPCTestClientBase.h>
30 
31 namespace ripple {
32 namespace test {
33 
34 class Submit_test : public beast::unit_test::suite
35 {
36 public:
38  {
39  public:
40  org::xrpl::rpc::v1::SubmitTransactionRequest request;
41  org::xrpl::rpc::v1::SubmitTransactionResponse reply;
42 
43  explicit SubmitClient(std::string const& port)
44  : GRPCTestClientBase(port)
45  {
46  }
47 
48  void
50  {
51  status = stub_->SubmitTransaction(&context, request, &reply);
52  }
53  };
54 
55  struct TestData
56  {
61  const static int fund = 10000;
62  } testData;
63 
64  void
66  {
67  testcase("fill test data");
68 
69  using namespace jtx;
70  Env env(*this, envconfig(addGrpcConfig));
71  auto const alice = Account("alice");
72  auto const bob = Account("bob");
73  env.fund(XRP(TestData::fund), "alice", "bob");
74  env.trust(bob["USD"](TestData::fund), alice);
75  env.close();
76 
77  auto toBinary = [this](std::string const& text) {
78  auto blob = strUnHex(text);
79  BEAST_EXPECT(blob);
80  return std::string{
81  reinterpret_cast<char const*>(blob->data()), blob->size()};
82  };
83 
84  // use a websocket client to fill transaction blobs
85  auto wsc = makeWSClient(env.app().config());
86  {
87  Json::Value jrequestXrp;
88  jrequestXrp[jss::secret] = toBase58(generateSeed("alice"));
89  jrequestXrp[jss::tx_json] =
90  pay("alice", "bob", XRP(TestData::fund / 2));
91  Json::Value jreply_xrp = wsc->invoke("sign", jrequestXrp);
92 
93  if (!BEAST_EXPECT(jreply_xrp.isMember(jss::result)))
94  return;
95  if (!BEAST_EXPECT(jreply_xrp[jss::result].isMember(jss::tx_blob)))
96  return;
98  toBinary(jreply_xrp[jss::result][jss::tx_blob].asString());
99  if (!BEAST_EXPECT(jreply_xrp[jss::result].isMember(jss::tx_json)))
100  return;
101  if (!BEAST_EXPECT(
102  jreply_xrp[jss::result][jss::tx_json].isMember(jss::hash)))
103  return;
104  testData.xrpTxHash = toBinary(
105  jreply_xrp[jss::result][jss::tx_json][jss::hash].asString());
106  }
107  {
108  Json::Value jrequestUsd;
109  jrequestUsd[jss::secret] = toBase58(generateSeed("bob"));
110  jrequestUsd[jss::tx_json] =
111  pay("bob", "alice", bob["USD"](TestData::fund / 2));
112  Json::Value jreply_usd = wsc->invoke("sign", jrequestUsd);
113 
114  if (!BEAST_EXPECT(jreply_usd.isMember(jss::result)))
115  return;
116  if (!BEAST_EXPECT(jreply_usd[jss::result].isMember(jss::tx_blob)))
117  return;
119  toBinary(jreply_usd[jss::result][jss::tx_blob].asString());
120  if (!BEAST_EXPECT(jreply_usd[jss::result].isMember(jss::tx_json)))
121  return;
122  if (!BEAST_EXPECT(
123  jreply_usd[jss::result][jss::tx_json].isMember(jss::hash)))
124  return;
125  testData.usdTxHash = toBinary(
126  jreply_usd[jss::result][jss::tx_json][jss::hash].asString());
127  }
128  }
129 
130  void
132  {
133  testcase("Submit good blobs, XRP, USD, and same transaction twice");
134 
135  using namespace jtx;
137  std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
138  Env env(*this, std::move(config));
139  auto const alice = Account("alice");
140  auto const bob = Account("bob");
141  env.fund(XRP(TestData::fund), "alice", "bob");
142  env.trust(bob["USD"](TestData::fund), alice);
143  env.close();
144 
145  auto getClient = [&grpcPort]() { return SubmitClient(grpcPort); };
146 
147  // XRP
148  {
149  auto client = getClient();
150  client.request.set_signed_transaction(testData.xrpTxBlob);
151  client.SubmitTransaction();
152  if (!BEAST_EXPECT(client.status.ok()))
153  {
154  return;
155  }
156  BEAST_EXPECT(client.reply.engine_result().result() == "tesSUCCESS");
157  BEAST_EXPECT(client.reply.engine_result_code() == 0);
158  BEAST_EXPECT(client.reply.hash() == testData.xrpTxHash);
159  }
160  // USD
161  {
162  auto client = getClient();
163  client.request.set_signed_transaction(testData.usdTxBlob);
164  client.SubmitTransaction();
165  if (!BEAST_EXPECT(client.status.ok()))
166  {
167  return;
168  }
169  BEAST_EXPECT(client.reply.engine_result().result() == "tesSUCCESS");
170  BEAST_EXPECT(client.reply.engine_result_code() == 0);
171  BEAST_EXPECT(client.reply.hash() == testData.usdTxHash);
172  }
173  // USD, error, same transaction again
174  {
175  auto client = getClient();
176  client.request.set_signed_transaction(testData.usdTxBlob);
177  client.SubmitTransaction();
178  if (!BEAST_EXPECT(client.status.ok()))
179  {
180  return;
181  }
182  BEAST_EXPECT(
183  client.reply.engine_result().result() == "tefPAST_SEQ");
184  BEAST_EXPECT(client.reply.engine_result_code() == -190);
185  }
186  }
187 
188  void
190  {
191  testcase("Submit error, bad blob, no account");
192 
193  using namespace jtx;
195  std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
196  Env env(*this, std::move(config));
197 
198  auto getClient = [&grpcPort]() { return SubmitClient(grpcPort); };
199 
200  // short transaction blob, cannot parse
201  {
202  auto client = getClient();
203  client.request.set_signed_transaction("deadbeef");
204  client.SubmitTransaction();
205  BEAST_EXPECT(!client.status.ok());
206  }
207  // bad blob with correct length, cannot parse
208  {
209  auto client = getClient();
210  auto xrpTxBlobCopy(testData.xrpTxBlob);
211  std::reverse(xrpTxBlobCopy.begin(), xrpTxBlobCopy.end());
212  client.request.set_signed_transaction(xrpTxBlobCopy);
213  client.SubmitTransaction();
214  BEAST_EXPECT(!client.status.ok());
215  }
216  // good blob, can parse but no account
217  {
218  auto client = getClient();
219  client.request.set_signed_transaction(testData.xrpTxBlob);
220  client.SubmitTransaction();
221  if (!BEAST_EXPECT(client.status.ok()))
222  {
223  return;
224  }
225  BEAST_EXPECT(
226  client.reply.engine_result().result() == "terNO_ACCOUNT");
227  BEAST_EXPECT(client.reply.engine_result_code() == -96);
228  }
229  }
230 
231  void
233  {
234  testcase("Submit good blobs but insufficient funds");
235 
236  using namespace jtx;
238  std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
239  Env env(*this, std::move(config));
240 
241  auto const alice = Account("alice");
242  auto const bob = Account("bob");
243  // fund 1000 (TestData::fund/10) XRP, the transaction sends 5000
244  // (TestData::fund/2) XRP, so insufficient funds
245  env.fund(XRP(TestData::fund / 10), "alice", "bob");
246  env.trust(bob["USD"](TestData::fund), alice);
247  env.close();
248 
249  {
250  SubmitClient client(grpcPort);
251  client.request.set_signed_transaction(testData.xrpTxBlob);
252  client.SubmitTransaction();
253  if (!BEAST_EXPECT(client.status.ok()))
254  {
255  return;
256  }
257  BEAST_EXPECT(
258  client.reply.engine_result().result() == "tecUNFUNDED_PAYMENT");
259  BEAST_EXPECT(client.reply.engine_result_code() == 104);
260  }
261  }
262 
263  void
264  run() override
265  {
266  fillTestData();
270  }
271 };
272 
273 BEAST_DEFINE_TESTSUITE(Submit, app, ripple);
274 
275 } // namespace test
276 } // namespace ripple
ripple::test::Submit_test::TestData
Definition: Submit_test.cpp:55
ripple::test::Submit_test::SubmitClient
Definition: Submit_test.cpp:37
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
std::string
STL class.
ripple::test::Submit_test::testSubmitInsufficientFundsGrpc
void testSubmitInsufficientFundsGrpc()
Definition: Submit_test.cpp:232
ripple::test::GRPCTestClientBase::stub_
std::unique_ptr< org::xrpl::rpc::v1::XRPLedgerAPIService::Stub > stub_
Definition: GRPCTestClientBase.h:44
std::string::size
T size(T... args)
ripple::test::jtx::addGrpcConfig
std::unique_ptr< Config > addGrpcConfig(std::unique_ptr< Config >)
add a grpc address and port to config
Definition: envconfig.cpp:113
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::test::Submit_test::testSubmitErrorBlobGrpc
void testSubmitErrorBlobGrpc()
Definition: Submit_test.cpp:189
ripple::test::Submit_test::TestData::fund
const static int fund
Definition: Submit_test.cpp:61
ripple::test::Submit_test::TestData::usdTxHash
std::string usdTxHash
Definition: Submit_test.cpp:60
ripple::test::Submit_test::testData
struct ripple::test::Submit_test::TestData testData
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:241
std::reverse
T reverse(T... args)
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::test::Submit_test::SubmitClient::request
org::xrpl::rpc::v1::SubmitTransactionRequest request
Definition: Submit_test.cpp:40
ripple::test::GRPCTestClientBase::status
grpc::Status status
Definition: GRPCTestClientBase.h:42
ripple::test::Submit_test::SubmitClient::SubmitTransaction
void SubmitTransaction()
Definition: Submit_test.cpp:49
ripple::test::jtx::Env::trust
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:256
ripple::test::GRPCTestClientBase
Definition: GRPCTestClientBase.h:29
ripple::test::Submit_test::run
void run() override
Definition: Submit_test.cpp:264
ripple::Application::config
virtual Config & config()=0
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::test::Submit_test::SubmitClient::SubmitClient
SubmitClient(std::string const &port)
Definition: Submit_test.cpp:43
ripple::test::Submit_test
Definition: Submit_test.cpp:34
ripple::test::Submit_test::TestData::xrpTxHash
std::string xrpTxHash
Definition: Submit_test.cpp:58
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
ripple::test::GRPCTestClientBase::context
grpc::ClientContext context
Definition: GRPCTestClientBase.h:43
ripple::generateSeed
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition: Seed.cpp:69
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::jtx::pay
Json::Value pay(Account const &account, Account const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:225
ripple::test::Submit_test::fillTestData
void fillTestData()
Definition: Submit_test.cpp:65
ripple::test::makeWSClient
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition: WSClient.cpp:300
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::test::Submit_test::TestData::usdTxBlob
std::string usdTxBlob
Definition: Submit_test.cpp:59
std::unique_ptr
STL class.
ripple::test::Submit_test::SubmitClient::reply
org::xrpl::rpc::v1::SubmitTransactionResponse reply
Definition: Submit_test.cpp:41
ripple::strUnHex
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
Definition: StringUtilities.h:49
ripple::test::Submit_test::TestData::xrpTxBlob
std::string xrpTxBlob
Definition: Submit_test.cpp:57
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)
ripple::test::Submit_test::testSubmitGoodBlobGrpc
void testSubmitGoodBlobGrpc()
Definition: Submit_test.cpp:131