@@ -272,24 +272,48 @@ Env::trust(STAmount const& amount, Account const& account)
272
272
test.expect (balance (account) == start);
273
273
}
274
274
275
- std::pair<TER, bool >
275
+ Env::ParsedResult
276
276
Env::parseResult (Json::Value const & jr)
277
277
{
278
- TER ter;
279
- if (jr.isObject () && jr.isMember (jss::result) &&
280
- jr[jss::result].isMember (jss::engine_result_code))
281
- ter = TER::fromInt (jr[jss::result][jss::engine_result_code].asInt ());
278
+ auto error = [](ParsedResult& parsed, Json::Value const & object) {
279
+ // Use an error code that is not used anywhere in the transaction
280
+ // engine to distinguish this case.
281
+ parsed.ter = telENV_RPC_FAILED;
282
+ // Extract information about the error
283
+ if (!object.isObject ())
284
+ return ;
285
+ if (object.isMember (jss::error_code))
286
+ parsed.rpcCode =
287
+ safe_cast<error_code_i>(object[jss::error_code].asInt ());
288
+ if (object.isMember (jss::error_message))
289
+ parsed.rpcMessage = object[jss::error_message].asString ();
290
+ if (object.isMember (jss::error))
291
+ parsed.rpcError = object[jss::error].asString ();
292
+ if (object.isMember (jss::error_exception))
293
+ parsed.rpcException = object[jss::error_exception].asString ();
294
+ };
295
+ ParsedResult parsed;
296
+ if (jr.isObject () && jr.isMember (jss::result))
297
+ {
298
+ auto const & result = jr[jss::result];
299
+ if (result.isMember (jss::engine_result_code))
300
+ {
301
+ parsed.ter = TER::fromInt (result[jss::engine_result_code].asInt ());
302
+ parsed.rpcCode .emplace (rpcSUCCESS);
303
+ }
304
+ else
305
+ error (parsed, result);
306
+ }
282
307
else
283
- // Use an error code that is not used anywhere in the transaction engine
284
- // to distinguish this case.
285
- ter = telENV_RPC_FAILED;
286
- return std::make_pair (ter, isTesSuccess (ter) || isTecClaim (ter));
308
+ error (parsed, jr);
309
+
310
+ return parsed;
287
311
}
288
312
289
313
void
290
314
Env::submit (JTx const & jt)
291
315
{
292
- bool didApply ;
316
+ ParsedResult parsedResult ;
293
317
auto const jr = [&]() {
294
318
if (jt.stx )
295
319
{
@@ -298,28 +322,27 @@ Env::submit(JTx const& jt)
298
322
jt.stx ->add (s);
299
323
auto const jr = rpc (" submit" , strHex (s.slice ()));
300
324
301
- std::tie (ter_, didApply) = parseResult (jr);
325
+ parsedResult = parseResult (jr);
326
+ test.expect (parsedResult.ter , " ter uninitialized!" );
327
+ ter_ = parsedResult.ter .value_or (telENV_RPC_FAILED);
302
328
303
329
return jr;
304
330
}
305
331
else
306
332
{
307
333
// Parsing failed or the JTx is
308
334
// otherwise missing the stx field.
309
- ter_ = temMALFORMED;
310
- didApply = false ;
335
+ parsedResult.ter = ter_ = temMALFORMED;
311
336
312
337
return Json::Value ();
313
338
}
314
339
}();
315
- return postconditions (jt, ter_, didApply , jr);
340
+ return postconditions (jt, parsedResult , jr);
316
341
}
317
342
318
343
void
319
344
Env::sign_and_submit (JTx const & jt, Json::Value params)
320
345
{
321
- bool didApply;
322
-
323
346
auto const account = lookup (jt.jv [jss::Account].asString ());
324
347
auto const & passphrase = account.name ();
325
348
@@ -348,24 +371,55 @@ Env::sign_and_submit(JTx const& jt, Json::Value params)
348
371
if (!txid_.parseHex (jr[jss::result][jss::tx_json][jss::hash].asString ()))
349
372
txid_.zero ();
350
373
351
- std::tie (ter_, didApply) = parseResult (jr);
374
+ ParsedResult const parsedResult = parseResult (jr);
375
+ test.expect (parsedResult.ter , " ter uninitialized!" );
376
+ ter_ = parsedResult.ter .value_or (telENV_RPC_FAILED);
352
377
353
- return postconditions (jt, ter_, didApply , jr);
378
+ return postconditions (jt, parsedResult , jr);
354
379
}
355
380
356
381
void
357
382
Env::postconditions (
358
383
JTx const & jt,
359
- TER ter,
360
- bool didApply,
384
+ ParsedResult const & parsed,
361
385
Json::Value const & jr)
362
386
{
363
- if (jt.ter &&
364
- !test.expect (
365
- ter == *jt.ter ,
366
- " apply: Got " + transToken (ter) + " (" + transHuman (ter) +
367
- " ); Expected " + transToken (*jt.ter ) + " (" +
368
- transHuman (*jt.ter ) + " )" ))
387
+ bool bad = !test.expect (parsed.ter , " apply: No ter result!" );
388
+ bad =
389
+ (jt.ter && parsed.ter &&
390
+ !test.expect (
391
+ *parsed.ter == *jt.ter ,
392
+ " apply: Got " + transToken (*parsed.ter ) + " (" +
393
+ transHuman (*parsed.ter ) + " ); Expected " +
394
+ transToken (*jt.ter ) + " (" + transHuman (*jt.ter ) + " )" ));
395
+ using namespace std ::string_literals;
396
+ bad = (jt.rpcCode &&
397
+ !test.expect (
398
+ parsed.rpcCode == jt.rpcCode ->first &&
399
+ parsed.rpcMessage == jt.rpcCode ->second ,
400
+ " apply: Got RPC result " s +
401
+ (parsed.rpcCode
402
+ ? RPC::get_error_info (*parsed.rpcCode ).token .c_str ()
403
+ : " NO RESULT" ) +
404
+ " (" + parsed.rpcMessage + " ); Expected " +
405
+ RPC::get_error_info (jt.rpcCode ->first ).token .c_str () + " (" +
406
+ jt.rpcCode ->second + " )" )) ||
407
+ bad;
408
+ // If we have an rpcCode (just checked), then the rpcException check is
409
+ // optional - the 'error' field may not be defined, but if it is, it must
410
+ // match rpcError.
411
+ bad =
412
+ (jt.rpcException &&
413
+ !test.expect (
414
+ (jt.rpcCode && parsed.rpcError .empty ()) ||
415
+ (parsed.rpcError == jt.rpcException ->first &&
416
+ (!jt.rpcException ->second ||
417
+ parsed.rpcException == *jt.rpcException ->second )),
418
+ " apply: Got RPC result " s + parsed.rpcError + " (" +
419
+ parsed.rpcException + " ); Expected " + jt.rpcException ->first +
420
+ " (" + jt.rpcException ->second .value_or (" n/a" ) + " )" )) ||
421
+ bad;
422
+ if (bad)
369
423
{
370
424
test.log << pretty (jt.jv ) << std::endl;
371
425
if (jr)
0 commit comments