mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-19 18:15:50 +00:00
Merge pull request #68 from Xahau/denis-import
[WIP] `Import` Quality Control (tests & minor fixes)
This commit is contained in:
13
.vscode/settings.json
vendored
Normal file
13
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"C_Cpp.formatting": "clangFormat",
|
||||
"C_Cpp.clang_format_path": "/Users/dustedfloor/projects/transia-rnd/rippled-icv2/.clang-format",
|
||||
"C_Cpp.clang_format_fallbackStyle": "{ ColumnLimit: 0 }",
|
||||
"[cpp]":{
|
||||
"editor.wordBasedSuggestions": false,
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.semanticHighlighting.enabled": true,
|
||||
"editor.tabSize": 4,
|
||||
"editor.defaultFormatter": "xaver.clang-format",
|
||||
"editor.formatOnSave": false
|
||||
}
|
||||
}
|
||||
@@ -703,6 +703,7 @@ if (tests)
|
||||
src/test/app/Flow_test.cpp
|
||||
src/test/app/Freeze_test.cpp
|
||||
src/test/app/HashRouter_test.cpp
|
||||
src/test/app/Import_test.cpp
|
||||
src/test/app/LedgerHistory_test.cpp
|
||||
src/test/app/LedgerLoad_test.cpp
|
||||
src/test/app/LedgerMaster_test.cpp
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
NIH dep: wasmedge: web assembly runtime for hooks.
|
||||
#]===================================================================]
|
||||
|
||||
find_package(Curses REQUIRED)
|
||||
include_directories(${CURSES_INCLUDE_DIR})
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
@@ -24,6 +26,7 @@ ExternalProject_Add (wasmedge_src
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
-DLLVM_DIR=${LLVM_DIR}
|
||||
-DLLVM_LIBRARY_DIR=${LLVM_LIBRARY_DIR}
|
||||
-DLLVM_ENABLE_TERMINFO=OFF
|
||||
$<$<NOT:$<BOOL:${is_multiconfig}>>:-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}>
|
||||
$<$<BOOL:${MSVC}>:
|
||||
"-DCMAKE_C_FLAGS=-GR -Gd -fp:precise -FS -MP -march=native"
|
||||
@@ -63,4 +66,6 @@ set_target_properties (wasmedge PROPERTIES
|
||||
"${wasmedge_src_BINARY_DIR}/include/api/"
|
||||
)
|
||||
target_link_libraries (ripple_libs INTERFACE wasmedge)
|
||||
target_link_libraries(ripple_libs INTERFACE ${CURSES_LIBRARY})
|
||||
target_link_libraries(ripple_libs INTERFACE xar)
|
||||
add_library (NIH::WasmEdge ALIAS wasmedge)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Import.h>
|
||||
#include <ripple/app/tx/impl/Import.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
@@ -61,412 +62,6 @@ Import::makeTxConsequences(PreflightContext const& ctx)
|
||||
return TxConsequences{ctx.tx, calculate(ctx)};
|
||||
}
|
||||
|
||||
inline bool
|
||||
isHex(std::string const& str)
|
||||
{
|
||||
return str.find_first_not_of("0123456789abcdefABCDEF") == std::string::npos;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isBase58(std::string const& str)
|
||||
{
|
||||
return str.find_first_not_of(
|
||||
"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz") ==
|
||||
std::string::npos;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isBase64(std::string const& str)
|
||||
{
|
||||
return str.find_first_not_of(
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"
|
||||
"/=") == std::string::npos;
|
||||
}
|
||||
|
||||
inline std::optional<uint64_t>
|
||||
parse_uint64(std::string const& str)
|
||||
{
|
||||
uint64_t result;
|
||||
auto [ptr, ec] =
|
||||
std::from_chars(str.data(), str.data() + str.size(), result);
|
||||
|
||||
if (ec == std::errc())
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
inline bool
|
||||
syntaxCheckProof(Json::Value const& proof, beast::Journal const& j, int depth = 0)
|
||||
{
|
||||
if (depth > 64)
|
||||
return false;
|
||||
|
||||
if (proof.isArray())
|
||||
{
|
||||
// List form
|
||||
if (proof.size() != 16)
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof list should be exactly 16 entries";
|
||||
return false;
|
||||
}
|
||||
for (const auto& entry : proof)
|
||||
{
|
||||
if (entry.isString())
|
||||
{
|
||||
if (!isHex(entry.asString()) || entry.asString().size() != 64)
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof list entry missing "
|
||||
"or wrong format "
|
||||
<< "(should be hex string with 64 characters)";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (entry.isArray())
|
||||
{
|
||||
if (!syntaxCheckProof(entry, j, depth + 1))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.transaction.proof list entry has wrong format";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (proof.isObject())
|
||||
{
|
||||
// Tree form
|
||||
if (depth == 0) // root is special case
|
||||
{
|
||||
if (!proof["hash"].isString() ||
|
||||
proof["hash"].asString().size() != 64 ||
|
||||
!proof["key"].isString() ||
|
||||
proof["key"].asString().size() != 64 ||
|
||||
!proof["children"].isObject())
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.transaction.proof tree node has wrong format (root)";
|
||||
return false;
|
||||
}
|
||||
|
||||
return syntaxCheckProof(proof["children"], j, depth + 1);
|
||||
}
|
||||
|
||||
for (const auto& branch : proof.getMemberNames())
|
||||
{
|
||||
if (branch.size() != 1 || !isHex(branch))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof child node was not 0-F "
|
||||
"hex nibble";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& node = proof[branch];
|
||||
if (!node.isObject() || !node["hash"].isString() ||
|
||||
node["hash"].asString().size() != 64 ||
|
||||
!node["key"].isString() ||
|
||||
node["key"].asString().size() != 64 ||
|
||||
!node["children"].isObject())
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.transaction.proof tree node has wrong format";
|
||||
return false;
|
||||
}
|
||||
if (!syntaxCheckProof(node["children"], j, depth + 1))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof has wrong format (should be "
|
||||
"array or object)";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// does not check signature etc
|
||||
inline
|
||||
std::optional<Json::Value>
|
||||
syntaxCheckXPOP(Blob const& blob, beast::Journal const& j)
|
||||
{
|
||||
if (blob.empty())
|
||||
return {};
|
||||
|
||||
std::string strJson(blob.begin(), blob.end());
|
||||
if (strJson.empty())
|
||||
return {};
|
||||
|
||||
try
|
||||
{
|
||||
Json::Value xpop;
|
||||
Json::Reader reader;
|
||||
|
||||
if (!reader.parse(strJson, xpop))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP isn't a string";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop.isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP is not a JSON object";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger is not a JSON object";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["transaction"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction is not a JSON object";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["validation"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation is not a JSON object";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["acroot"].isString() ||
|
||||
xpop["ledger"]["acroot"].asString().size() != 64 ||
|
||||
!isHex(xpop["ledger"]["acroot"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.acroot missing or wrong format (should "
|
||||
"be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["txroot"].isString() ||
|
||||
xpop["ledger"]["txroot"].asString().size() != 64 ||
|
||||
!isHex(xpop["ledger"]["txroot"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.txroot missing or wrong format "
|
||||
"(should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["phash"].isString() ||
|
||||
xpop["ledger"]["phash"].asString().size() != 64 ||
|
||||
!isHex(xpop["ledger"]["phash"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.phash missing or wrong format "
|
||||
"(should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["close"].isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.close missing or wrong format "
|
||||
"(should be int)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["coins"].isInt())
|
||||
{
|
||||
// pass
|
||||
}
|
||||
else if (xpop["ledger"]["coins"].isString())
|
||||
{
|
||||
if (!parse_uint64(xpop["ledger"]["coins"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.coins missing or wrong format "
|
||||
"(should be int or string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.coins missing or wrong format "
|
||||
"(should be int or string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["cres"].isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.cres missing or wrong format "
|
||||
"(should be int)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["flags"].isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.flags missing or wrong format "
|
||||
"(should be int)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["pclose"].isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.pclose missing or wrong format "
|
||||
"(should be int)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["transaction"]["blob"].isString() ||
|
||||
!isHex(xpop["transaction"]["blob"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.blob missing or wrong format "
|
||||
"(should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["transaction"]["meta"].isString() ||
|
||||
!isHex(xpop["transaction"]["meta"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.meta missing or wrong format "
|
||||
"(should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!syntaxCheckProof(xpop["transaction"]["proof"], j))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof failed syntax check "
|
||||
"(tree/list form)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["validation"]["data"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.data missing or wrong format "
|
||||
"(should be JSON object)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["validation"]["unl"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl missing or wrong format "
|
||||
"(should be JSON object)";
|
||||
return {};
|
||||
}
|
||||
|
||||
for (const auto& key : xpop["validation"]["data"].getMemberNames())
|
||||
{
|
||||
const auto& value = xpop["validation"]["data"][key];
|
||||
if (!isBase58(key) || !value.isString() || !isHex(value.asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.data entry has wrong format "
|
||||
<< "(key should be base58 string and value "
|
||||
"should be hex string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& key : xpop["validation"]["unl"].getMemberNames())
|
||||
{
|
||||
const auto& value = xpop["validation"]["unl"][key];
|
||||
if (key == "public_key")
|
||||
{
|
||||
if (!value.isString() || !isHex(value.asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.public_key missing or "
|
||||
"wrong format (should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto pk = strUnHex(value.asString());
|
||||
|
||||
if (!publicKeyType(makeSlice(*pk)))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.public_key invalid key type.";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (key == "manifest")
|
||||
{
|
||||
if (!value.isString())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.manifest missing or "
|
||||
"wrong format (should be string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (key == "blob")
|
||||
{
|
||||
if (!value.isString() || !isBase64(value.asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.blob missing or wrong "
|
||||
"format (should be base64 string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (key == "signature")
|
||||
{
|
||||
if (!value.isString() || !isHex(value.asString()))
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.validation.unl.signature missing or wrong "
|
||||
"format (should be hex string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (key == "version")
|
||||
{
|
||||
if (!value.isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.version missing or "
|
||||
"wrong format (should be int)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!value.isObject() && !value.isString())
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.validation.unl entry has wrong format";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return xpop;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
JLOG(j.warn()) << "An exception occurred during XPOP validation";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline
|
||||
std::optional<
|
||||
std::pair<
|
||||
uint32_t, // sequence
|
||||
PublicKey // master key
|
||||
>>
|
||||
getVLInfo(Json::Value const& xpop, beast::Journal const& j)
|
||||
{
|
||||
auto const data = base64_decode(xpop[jss::validation][jss::unl][jss::blob].asString());
|
||||
Json::Reader r;
|
||||
Json::Value list;
|
||||
if (!r.parse(data, list))
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "Import: unl blob was not valid json (after base64 decoding)";
|
||||
return {};
|
||||
}
|
||||
auto const sequence = list[jss::sequence].asUInt();
|
||||
|
||||
auto const m =
|
||||
deserializeManifest(base64_decode(xpop[jss::validation][jss::unl][jss::manifest].asString()));
|
||||
|
||||
if (!m)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "Import: failed to deserialize manifest";
|
||||
return {};
|
||||
}
|
||||
|
||||
return { {sequence, m->masterKey} };
|
||||
}
|
||||
|
||||
|
||||
std::pair<
|
||||
std::unique_ptr<STTx const>, // txn
|
||||
@@ -535,6 +130,14 @@ Import::preflight(PreflightContext const& ctx)
|
||||
|
||||
auto& tx = ctx.tx;
|
||||
|
||||
if (tx.getFieldAmount(sfFee) != beast::zero)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: sfFee must be 0 "
|
||||
<< tx.getTransactionID();
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (tx.getFieldVL(sfBlob).size() > (512 * 1024))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
@@ -554,7 +157,7 @@ Import::preflight(PreflightContext const& ctx)
|
||||
}
|
||||
|
||||
// parse blob as json
|
||||
auto const xpop =
|
||||
auto const xpop =
|
||||
syntaxCheckXPOP(tx.getFieldVL(sfBlob), ctx.j);
|
||||
|
||||
if (!xpop)
|
||||
@@ -778,13 +381,31 @@ Import::preflight(PreflightContext const& ctx)
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (!(list.isMember(jss::sequence) && list[jss::sequence].isInt() &&
|
||||
list.isMember(jss::expiration) && list[jss::expiration].isInt() &&
|
||||
(!list.isMember(jss::effective) || list[jss::effective].isInt()) &&
|
||||
list.isMember(jss::validators) && list[jss::validators].isArray()))
|
||||
if (!list.isMember(jss::sequence) || !list[jss::sequence].isInt())
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: unl blob json (after base64 decoding) lacked required fields and/or types "
|
||||
<< "Import: unl blob json (after base64 decoding) lacked required field (sequence) and/or types "
|
||||
<< tx.getTransactionID();
|
||||
return temMALFORMED;
|
||||
}
|
||||
if (!list.isMember(jss::expiration) || !list[jss::expiration].isInt())
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: unl blob json (after base64 decoding) lacked required field (expiration) and/or types "
|
||||
<< tx.getTransactionID();
|
||||
return temMALFORMED;
|
||||
}
|
||||
if (list.isMember(jss::effective) && !list[jss::effective].isInt())
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: unl blob json (after base64 decoding) lacked required field (effective) and/or types "
|
||||
<< tx.getTransactionID();
|
||||
return temMALFORMED;
|
||||
}
|
||||
if (!list.isMember(jss::validators) || !list[jss::validators].isArray())
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: unl blob json (after base64 decoding) lacked required field (validators) and/or types "
|
||||
<< tx.getTransactionID();
|
||||
return temMALFORMED;
|
||||
}
|
||||
@@ -838,7 +459,7 @@ Import::preflight(PreflightContext const& ctx)
|
||||
if (!([](Json::Value const& proof, std::string hash) -> bool
|
||||
{
|
||||
auto const proofContains =
|
||||
[](Json::Value const* proof, std::string hash, int depth = 0, auto proofContains) -> bool
|
||||
[](Json::Value const* proof, std::string hash, int depth = 0, auto proofContains = nullptr) -> bool
|
||||
{
|
||||
if (depth > 32)
|
||||
return false;
|
||||
@@ -885,7 +506,7 @@ Import::preflight(PreflightContext const& ctx)
|
||||
([](Json::Value const& proof) -> uint256
|
||||
{
|
||||
auto hashProof =
|
||||
[](Json::Value const& proof, int depth = 0, auto const& hashProof) -> uint256
|
||||
[](Json::Value const& proof, int depth = 0, auto const& hashProof = nullptr) -> uint256
|
||||
{
|
||||
const uint256 nullhash;
|
||||
|
||||
@@ -918,6 +539,7 @@ Import::preflight(PreflightContext const& ctx)
|
||||
|
||||
for (int x = 0; x < 16; ++x)
|
||||
{
|
||||
// Duplicate / Sanity
|
||||
std::string const nibble (1, "0123456789ABCDEF"[x]);
|
||||
if (!proof[jss::children].isMember(nibble))
|
||||
hash_append(h, nullhash);
|
||||
@@ -948,6 +570,7 @@ Import::preflight(PreflightContext const& ctx)
|
||||
auto coins = parse_uint64(lgr[jss::coins].asString());
|
||||
uint256 phash, acroot;
|
||||
|
||||
// Duplicate / Sanity - syntaxCheckXPOP
|
||||
if (!coins ||
|
||||
!phash.parseHex(lgr[jss::phash].asString()) ||
|
||||
!acroot.parseHex(lgr[jss::acroot].asString()))
|
||||
@@ -1044,11 +667,18 @@ Import::preflight(PreflightContext const& ctx)
|
||||
validatorsMaster[nodemaster] = nodepub;
|
||||
}
|
||||
|
||||
JLOG(ctx.j.trace())
|
||||
<< "totalValidatorCount: " << totalValidatorCount;
|
||||
|
||||
uint64_t quorum = totalValidatorCount * 0.8;
|
||||
|
||||
if (quorum == 0)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: resetting quorum to 1. "
|
||||
<< tx.getTransactionID();
|
||||
quorum = 1;
|
||||
|
||||
}
|
||||
|
||||
// count how many validations this ledger hash has
|
||||
|
||||
@@ -1185,7 +815,7 @@ Import::preflight(PreflightContext const& ctx)
|
||||
// if it is less than the Account Sequence in the xpop then mint and
|
||||
// update sfImportSequence
|
||||
|
||||
|
||||
// Duplicate / Sanity
|
||||
if (!stpTrans->isFieldPresent(sfSequence) ||
|
||||
!stpTrans->isFieldPresent(sfFee) ||
|
||||
!isXRP(stpTrans->getFieldAmount(sfFee)))
|
||||
@@ -1195,13 +825,11 @@ Import::preflight(PreflightContext const& ctx)
|
||||
<< tx.getTransactionID();
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
return preflight2(ctx);
|
||||
}
|
||||
|
||||
// RH TODO: manifest serials should be kept on chain
|
||||
|
||||
|
||||
TER
|
||||
Import::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
@@ -1238,14 +866,14 @@ Import::preclaim(PreclaimContext const& ctx)
|
||||
return tefPAST_IMPORT_SEQ;
|
||||
}
|
||||
|
||||
auto const vl = getVLInfo(*xpop, ctx.j);
|
||||
auto const vlInfo = getVLInfo(*xpop, ctx.j);
|
||||
|
||||
if (!vl)
|
||||
if (!vlInfo)
|
||||
return tefINTERNAL;
|
||||
|
||||
auto const& sleVL = ctx.view.read(keylet::import_vlseq(vl->second));
|
||||
auto const& sleVL = ctx.view.read(keylet::import_vlseq(vlInfo->second));
|
||||
|
||||
if (sleVL && sleVL->getFieldU32(sfImportSequence) > vl->first)
|
||||
if (sleVL && sleVL->getFieldU32(sfImportSequence) > vlInfo->first)
|
||||
return tefPAST_IMPORT_VL_SEQ;
|
||||
|
||||
return tesSUCCESS;
|
||||
@@ -1282,6 +910,7 @@ Import::doApply()
|
||||
auto const id = ctx_.tx[sfAccount];
|
||||
|
||||
auto const k = keylet::account(id);
|
||||
auto const ksl = keylet::signers(id);
|
||||
|
||||
auto sle = view().peek(k);
|
||||
|
||||
@@ -1292,17 +921,18 @@ Import::doApply()
|
||||
|
||||
auto const tt = stpTrans->getTxnType();
|
||||
|
||||
|
||||
// rekeying is only allowed on a tesSUCCESS, but minting is allowed on any tes or tec code.
|
||||
if (meta->getFieldU8(sfTransactionResult) == tesSUCCESS)
|
||||
{
|
||||
if (tt == ttSIGNER_LIST_SET)
|
||||
{
|
||||
JLOG(ctx_.journal.warn()) << "SingerListSet";
|
||||
// key import: signer list
|
||||
setSignerEntries = stpTrans->getFieldArray(sfSignerEntries);
|
||||
}
|
||||
else if (tt == ttREGULAR_KEY_SET)
|
||||
{
|
||||
JLOG(ctx_.journal.warn()) << "SetRegularKey";
|
||||
// key import: regular key
|
||||
setRegularKey = stpTrans->getAccountID(sfRegularKey);
|
||||
}
|
||||
@@ -1310,9 +940,10 @@ Import::doApply()
|
||||
|
||||
bool const create = !sle;
|
||||
|
||||
if (!sle)
|
||||
if (create)
|
||||
{
|
||||
// Create the account.
|
||||
JLOG(ctx_.journal.warn()) << "create - create account";
|
||||
std::uint32_t const seqno{
|
||||
view().rules().enabled(featureDeletableAccounts) ? view().seq()
|
||||
: 1};
|
||||
@@ -1332,6 +963,8 @@ Import::doApply()
|
||||
return tefINTERNAL;
|
||||
}
|
||||
|
||||
JLOG(ctx_.journal.warn()) << "Import: inital balance" << initBal;
|
||||
|
||||
sle->setFieldAmount(sfBalance, initBal);
|
||||
|
||||
}
|
||||
@@ -1343,16 +976,25 @@ Import::doApply()
|
||||
return tefINTERNAL;
|
||||
}
|
||||
|
||||
if (setSignerEntries)
|
||||
sle->setFieldArray(sfSignerEntries, *setSignerEntries);
|
||||
if (setSignerEntries) {
|
||||
JLOG(ctx_.journal.warn()) << "signer list set";
|
||||
auto signerList = std::make_shared<SLE>(ksl);
|
||||
signerList->setFieldArray(sfSignerEntries, *setSignerEntries);
|
||||
view().insert(signerList);
|
||||
}
|
||||
else if (setRegularKey)
|
||||
{
|
||||
JLOG(ctx_.journal.warn()) << "set regular key";
|
||||
sle->setAccountID(sfRegularKey, *setRegularKey);
|
||||
}
|
||||
|
||||
if (create)
|
||||
{
|
||||
JLOG(ctx_.journal.warn()) << "create";
|
||||
if (!signedWithMaster)
|
||||
{
|
||||
// disable master if the account is created using non-master key
|
||||
JLOG(ctx_.journal.warn()) << "create - disable master";
|
||||
sle->setAccountID(sfRegularKey, noAccount());
|
||||
sle->setFieldU32(sfFlags, lsfDisableMaster);
|
||||
}
|
||||
@@ -1361,11 +1003,13 @@ Import::doApply()
|
||||
else
|
||||
{
|
||||
// account already exists
|
||||
JLOG(ctx_.journal.warn()) << "update - import sequence";
|
||||
sle->setFieldU32(sfImportSequence, importSequence);
|
||||
|
||||
// credit the PoB
|
||||
if (burn > beast::zero)
|
||||
{
|
||||
JLOG(ctx_.journal.warn()) << "update - credit burn";
|
||||
STAmount startBal = sle->getFieldAmount(sfBalance);
|
||||
STAmount finalBal = startBal + burn;
|
||||
if (finalBal > startBal)
|
||||
@@ -1388,6 +1032,7 @@ Import::doApply()
|
||||
if (!sleVL)
|
||||
{
|
||||
// create VL import seq counter
|
||||
JLOG(ctx_.journal.warn()) << "create vl seq - insert import sequence + public key";
|
||||
sleVL = std::make_shared<SLE>(keyletVL);
|
||||
sleVL->setFieldU32(sfImportSequence, infoVL->first);
|
||||
sleVL->setFieldVL(sfPublicKey, infoVL->second.slice());
|
||||
@@ -1395,6 +1040,7 @@ Import::doApply()
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(ctx_.journal.warn()) << "update vl - insert import sequence";
|
||||
uint32_t current = sleVL->getFieldU32(sfImportSequence);
|
||||
|
||||
if (current > infoVL->first)
|
||||
@@ -1421,6 +1067,7 @@ XRPAmount
|
||||
Import::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
return XRPAmount { 0 } ;
|
||||
// return Transactor::calculateBaseFee(view, tx);
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -167,10 +167,10 @@ XRPNotCreated::finalize(
|
||||
dropsAdded += Import::INITIAL_IMPORT_XRP; // welcome amount for new imports
|
||||
|
||||
JLOG(j.trace())
|
||||
<< "Invariant XRPNotCreated Import: "
|
||||
<< "Invariant XRPNotCreated Import: "
|
||||
<< "dropsAdded: " << dropsAdded
|
||||
<< " fee.drops(): " << fee.drops()
|
||||
<< " drops_: " << drops_
|
||||
<< " fee.drops(): " << fee.drops()
|
||||
<< " drops_: " << drops_
|
||||
<< " dropsAdded - fee.drops(): " << dropsAdded - fee.drops();
|
||||
|
||||
return (drops_ == dropsAdded.drops() - fee.drops());
|
||||
|
||||
@@ -79,7 +79,7 @@ Invoke::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
XRPAmount extraFee{0};
|
||||
|
||||
if (tx.isFieldPresent(sfBlob))
|
||||
extraFee += XRPAmount{ tx.getFieldVL(sfBlob).size() };
|
||||
extraFee += XRPAmount{ static_cast<XRPAmount>(tx.getFieldVL(sfBlob).size()) };
|
||||
|
||||
if (tx.isFieldPresent(sfHookParameters))
|
||||
{
|
||||
@@ -93,7 +93,7 @@ Invoke::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
(param.isFieldPresent(sfHookParameterValue) ?
|
||||
param.getFieldVL(sfHookParameterValue).size() : 0);
|
||||
}
|
||||
extraFee += XRPAmount { paramBytes };
|
||||
extraFee += XRPAmount { static_cast<XRPAmount>(paramBytes) };
|
||||
}
|
||||
|
||||
return Transactor::calculateBaseFee(view, tx) + extraFee;
|
||||
|
||||
@@ -547,7 +547,7 @@ SetHook::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
(param.isFieldPresent(sfHookParameterValue) ?
|
||||
param.getFieldVL(sfHookParameterValue).size() : 0);
|
||||
}
|
||||
extraFee += XRPAmount { paramBytes };
|
||||
extraFee += XRPAmount { static_cast<XRPAmount>(paramBytes) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
436
src/ripple/protocol/Import.h
Normal file
436
src/ripple/protocol/Import.h
Normal file
@@ -0,0 +1,436 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PROTOCOL_IMPORT_H_INCLUDED
|
||||
#define RIPPLE_PROTOCOL_IMPORT_H_INCLUDED
|
||||
|
||||
// #include <ripple/basics/Log.h>
|
||||
#include <charconv>
|
||||
#include <ripple/app/misc/Manifest.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/basics/base64.h>
|
||||
#include <ripple/json/json_reader.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
inline bool
|
||||
isHex(std::string const& str)
|
||||
{
|
||||
return str.find_first_not_of("0123456789abcdefABCDEF") == std::string::npos;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isBase58(std::string const& str)
|
||||
{
|
||||
return str.find_first_not_of(
|
||||
"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz") ==
|
||||
std::string::npos;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isBase64(std::string const& str)
|
||||
{
|
||||
return str.find_first_not_of(
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"
|
||||
"/=") == std::string::npos;
|
||||
}
|
||||
|
||||
inline std::optional<uint64_t>
|
||||
parse_uint64(std::string const& str)
|
||||
{
|
||||
uint64_t result;
|
||||
auto [ptr, ec] =
|
||||
std::from_chars(str.data(), str.data() + str.size(), result);
|
||||
|
||||
if (ec == std::errc())
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
inline bool
|
||||
syntaxCheckProof(Json::Value const& proof, beast::Journal const& j, int depth = 0)
|
||||
{
|
||||
if (depth > 64) {
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof list should be less than 64 entries";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (proof.isArray())
|
||||
{
|
||||
// List form
|
||||
if (proof.size() != 16)
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof list should be exactly 16 entries";
|
||||
return false;
|
||||
}
|
||||
for (const auto& entry : proof)
|
||||
{
|
||||
if (entry.isString())
|
||||
{
|
||||
if (!isHex(entry.asString()) || entry.asString().size() != 64)
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof list entry missing "
|
||||
"or wrong format "
|
||||
<< "(should be hex string with 64 characters)";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (entry.isArray())
|
||||
{
|
||||
if (!syntaxCheckProof(entry, j, depth + 1))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.transaction.proof list entry has wrong format";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (proof.isObject())
|
||||
{
|
||||
// Tree form
|
||||
if (depth == 0) // root is special case
|
||||
{
|
||||
if (!proof["hash"].isString() ||
|
||||
proof["hash"].asString().size() != 64 ||
|
||||
!proof["key"].isString() ||
|
||||
proof["key"].asString().size() != 64 ||
|
||||
!proof["children"].isObject())
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.transaction.proof tree node has wrong format (root)";
|
||||
return false;
|
||||
}
|
||||
|
||||
return syntaxCheckProof(proof["children"], j, depth + 1);
|
||||
}
|
||||
|
||||
for (const auto& branch : proof.getMemberNames())
|
||||
{
|
||||
if (branch.size() != 1 || !isHex(branch))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof child node was not 0-F "
|
||||
"hex nibble";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& node = proof[branch];
|
||||
if (!node.isObject() || !node["hash"].isString() ||
|
||||
node["hash"].asString().size() != 64 ||
|
||||
!node["key"].isString() ||
|
||||
node["key"].asString().size() != 64 ||
|
||||
!node["children"].isObject())
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.transaction.proof tree node has wrong format";
|
||||
return false;
|
||||
}
|
||||
if (!syntaxCheckProof(node["children"], j, depth + 1)) {
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.transaction.proof bad children format";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof has wrong format (should be "
|
||||
"array or object)";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// does not check signature etc
|
||||
inline
|
||||
std::optional<Json::Value>
|
||||
syntaxCheckXPOP(Blob const& blob, beast::Journal const& j)
|
||||
{
|
||||
if (blob.empty())
|
||||
return {};
|
||||
|
||||
std::string strJson(blob.begin(), blob.end());
|
||||
if (strJson.empty())
|
||||
return {};
|
||||
|
||||
try
|
||||
{
|
||||
Json::Value xpop;
|
||||
Json::Reader reader;
|
||||
|
||||
if (!reader.parse(strJson, xpop))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP failed to parse string json";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop.isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP is not a JSON object";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger is not a JSON object";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["transaction"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction is not a JSON object";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["validation"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation is not a JSON object";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["acroot"].isString() ||
|
||||
xpop["ledger"]["acroot"].asString().size() != 64 ||
|
||||
!isHex(xpop["ledger"]["acroot"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.acroot missing or wrong format (should "
|
||||
"be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["txroot"].isString() ||
|
||||
xpop["ledger"]["txroot"].asString().size() != 64 ||
|
||||
!isHex(xpop["ledger"]["txroot"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.txroot missing or wrong format "
|
||||
"(should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["phash"].isString() ||
|
||||
xpop["ledger"]["phash"].asString().size() != 64 ||
|
||||
!isHex(xpop["ledger"]["phash"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.phash missing or wrong format "
|
||||
"(should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["close"].isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.close missing or wrong format "
|
||||
"(should be int)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (xpop["ledger"]["coins"].isInt())
|
||||
{
|
||||
// pass
|
||||
}
|
||||
else if (xpop["ledger"]["coins"].isString())
|
||||
{
|
||||
if (!parse_uint64(xpop["ledger"]["coins"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.coins missing or wrong format "
|
||||
"(should be int or string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.coins missing or wrong format "
|
||||
"(should be int or string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["cres"].isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.cres missing or wrong format "
|
||||
"(should be int)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["flags"].isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.flags missing or wrong format "
|
||||
"(should be int)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["ledger"]["pclose"].isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.ledger.pclose missing or wrong format "
|
||||
"(should be int)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["transaction"]["blob"].isString() ||
|
||||
!isHex(xpop["transaction"]["blob"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.blob missing or wrong format "
|
||||
"(should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["transaction"]["meta"].isString() ||
|
||||
!isHex(xpop["transaction"]["meta"].asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.meta missing or wrong format "
|
||||
"(should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!syntaxCheckProof(xpop["transaction"]["proof"], j))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.transaction.proof failed syntax check "
|
||||
"(tree/list form)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["validation"]["data"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.data missing or wrong format "
|
||||
"(should be JSON object)";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!xpop["validation"]["unl"].isObject())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl missing or wrong format "
|
||||
"(should be JSON object)";
|
||||
return {};
|
||||
}
|
||||
|
||||
for (const auto& key : xpop["validation"]["data"].getMemberNames())
|
||||
{
|
||||
const auto& value = xpop["validation"]["data"][key];
|
||||
if (!isBase58(key) || !value.isString() || !isHex(value.asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.data entry has wrong format "
|
||||
<< "(key should be base58 string and value "
|
||||
"should be hex string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& key : xpop["validation"]["unl"].getMemberNames())
|
||||
{
|
||||
const auto& value = xpop["validation"]["unl"][key];
|
||||
if (key == "public_key")
|
||||
{
|
||||
if (!value.isString() || !isHex(value.asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.public_key missing or "
|
||||
"wrong format (should be hex string)";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto pk = strUnHex(value.asString());
|
||||
|
||||
if (!publicKeyType(makeSlice(*pk)))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.public_key invalid key type.";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (key == "manifest")
|
||||
{
|
||||
if (!value.isString())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.manifest missing or "
|
||||
"wrong format (should be string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (key == "blob")
|
||||
{
|
||||
if (!value.isString() || !isBase64(value.asString()))
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.blob missing or wrong "
|
||||
"format (should be base64 string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (key == "signature")
|
||||
{
|
||||
if (!value.isString() || !isHex(value.asString()))
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.validation.unl.signature missing or wrong "
|
||||
"format (should be hex string)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (key == "version")
|
||||
{
|
||||
if (!value.isInt())
|
||||
{
|
||||
JLOG(j.warn()) << "XPOP.validation.unl.version missing or "
|
||||
"wrong format (should be int)";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!value.isObject() && !value.isString())
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "XPOP.validation.unl entry has wrong format";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
return xpop;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
JLOG(j.warn()) << "An exception occurred during XPOP validation";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// <sequence, master key>
|
||||
inline
|
||||
std::optional<std::pair< uint32_t, PublicKey>>
|
||||
getVLInfo(Json::Value const& xpop, beast::Journal const& j)
|
||||
{
|
||||
auto const data = base64_decode(xpop[jss::validation][jss::unl][jss::blob].asString());
|
||||
Json::Reader r;
|
||||
Json::Value list;
|
||||
if (!r.parse(data, list))
|
||||
{
|
||||
JLOG(j.warn()) << "Import: unl blob was not valid json (after base64 decoding)";
|
||||
return {};
|
||||
}
|
||||
auto const sequence = list[jss::sequence].asUInt();
|
||||
auto const m = deserializeManifest(base64_decode(xpop[jss::validation][jss::unl][jss::manifest].asString()));
|
||||
if (!m)
|
||||
{
|
||||
JLOG(j.warn()) << "Import: failed to deserialize manifest";
|
||||
return {};
|
||||
}
|
||||
return { {sequence, m->masterKey} };
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -131,6 +131,7 @@ transResults()
|
||||
MAKE_ERROR(telREQUIRES_NETWORK_ID, "Transactions submitted to this node/network must include a correct NetworkID field."),
|
||||
MAKE_ERROR(telNETWORK_ID_MAKES_TX_NON_CANONICAL, "Transactions submitted to this node/network must NOT include a NetworkID field."),
|
||||
MAKE_ERROR(telNON_LOCAL_EMITTED_TXN, "Emitted transaction cannot be applied because it was not generated locally."),
|
||||
MAKE_ERROR(telIMPORT_VL_KEY_NOT_RECOGNISED, "Import vl key was not recognized."),
|
||||
MAKE_ERROR(telCAN_NOT_QUEUE_IMPORT, "Import transaction was not able to be directly applied and cannot be queued."),
|
||||
|
||||
MAKE_ERROR(temMALFORMED, "Malformed transaction."),
|
||||
|
||||
@@ -69,281 +69,281 @@ private:
|
||||
Json::Value ret{Json::objectValue};
|
||||
ret[jss::TYPES] = Json::objectValue;
|
||||
|
||||
auto const translate = [](std::string inp) -> std::string {
|
||||
auto replace = [&](const char* f, const char* r) -> std::string {
|
||||
std::string out = inp;
|
||||
boost::replace_all(out, f, r);
|
||||
return out;
|
||||
};
|
||||
// auto const translate = [](std::string inp) -> std::string {
|
||||
// auto replace = [&](const char* f, const char* r) -> std::string {
|
||||
// std::string out = inp;
|
||||
// boost::replace_all(out, f, r);
|
||||
// return out;
|
||||
// };
|
||||
|
||||
auto find = [&](const char* s) -> bool {
|
||||
return inp.find(s) != std::string::npos;
|
||||
};
|
||||
// auto find = [&](const char* s) -> bool {
|
||||
// return inp.find(s) != std::string::npos;
|
||||
// };
|
||||
|
||||
if (find("UINT"))
|
||||
{
|
||||
if (find("256") || find("160") || find("128"))
|
||||
return replace("UINT", "Hash");
|
||||
else
|
||||
return replace("UINT", "UInt");
|
||||
}
|
||||
// if (find("UINT"))
|
||||
// {
|
||||
// if (find("256") || find("160") || find("128"))
|
||||
// return replace("UINT", "Hash");
|
||||
// else
|
||||
// return replace("UINT", "UInt");
|
||||
// }
|
||||
|
||||
if (inp == "OBJECT")
|
||||
return "STObject";
|
||||
if (inp == "ARRAY")
|
||||
return "STArray";
|
||||
if (inp == "AMM")
|
||||
return "AMM";
|
||||
if (inp == "ACCOUNT")
|
||||
return "AccountID";
|
||||
if (inp == "LEDGERENTRY")
|
||||
return "LedgerEntry";
|
||||
if (inp == "NOTPRESENT")
|
||||
return "NotPresent";
|
||||
if (inp == "PATHSET")
|
||||
return "PathSet";
|
||||
if (inp == "VL")
|
||||
return "Blob";
|
||||
if (inp == "DIR_NODE")
|
||||
return "DirectoryNode";
|
||||
if (inp == "PAYCHAN")
|
||||
return "PayChannel";
|
||||
// if (inp == "OBJECT")
|
||||
// return "STObject";
|
||||
// if (inp == "ARRAY")
|
||||
// return "STArray";
|
||||
// if (inp == "AMM")
|
||||
// return "AMM";
|
||||
// if (inp == "ACCOUNT")
|
||||
// return "AccountID";
|
||||
// if (inp == "LEDGERENTRY")
|
||||
// return "LedgerEntry";
|
||||
// if (inp == "NOTPRESENT")
|
||||
// return "NotPresent";
|
||||
// if (inp == "PATHSET")
|
||||
// return "PathSet";
|
||||
// if (inp == "VL")
|
||||
// return "Blob";
|
||||
// if (inp == "DIR_NODE")
|
||||
// return "DirectoryNode";
|
||||
// if (inp == "PAYCHAN")
|
||||
// return "PayChannel";
|
||||
|
||||
static const std::map<std::string, std::string>
|
||||
capitalization_exceptions = {
|
||||
{"NFTOKEN", "NFToken"},
|
||||
{"UNL", "UNL"},
|
||||
{"XCHAIN", "XChain"},
|
||||
{"ID", "ID"},
|
||||
{"AMM", "AMM"},
|
||||
{"URITOKEN", "URIToken"},
|
||||
{"URI", "URI"}};
|
||||
// static const std::map<std::string, std::string>
|
||||
// capitalization_exceptions = {
|
||||
// {"NFTOKEN", "NFToken"},
|
||||
// {"UNL", "UNL"},
|
||||
// {"XCHAIN", "XChain"},
|
||||
// {"ID", "ID"},
|
||||
// {"AMM", "AMM"},
|
||||
// {"URITOKEN", "URIToken"},
|
||||
// {"URI", "URI"}};
|
||||
|
||||
std::string out;
|
||||
size_t pos = 0;
|
||||
for (;;)
|
||||
{
|
||||
pos = inp.find("_");
|
||||
if (pos == std::string::npos)
|
||||
pos = inp.size();
|
||||
std::string token = inp.substr(0, pos);
|
||||
if (auto const e = capitalization_exceptions.find(token);
|
||||
e != capitalization_exceptions.end())
|
||||
out += e->second;
|
||||
else if (token.size() > 1)
|
||||
{
|
||||
boost::algorithm::to_lower(token);
|
||||
token.data()[0] -= ('a' - 'A');
|
||||
out += token;
|
||||
}
|
||||
else
|
||||
out += token;
|
||||
if (pos == inp.size())
|
||||
break;
|
||||
inp = inp.substr(pos + 1);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
// std::string out;
|
||||
// size_t pos = 0;
|
||||
// for (;;)
|
||||
// {
|
||||
// pos = inp.find("_");
|
||||
// if (pos == std::string::npos)
|
||||
// pos = inp.size();
|
||||
// std::string token = inp.substr(0, pos);
|
||||
// if (auto const e = capitalization_exceptions.find(token);
|
||||
// e != capitalization_exceptions.end())
|
||||
// out += e->second;
|
||||
// else if (token.size() > 1)
|
||||
// {
|
||||
// boost::algorithm::to_lower(token);
|
||||
// token.data()[0] -= ('a' - 'A');
|
||||
// out += token;
|
||||
// }
|
||||
// else
|
||||
// out += token;
|
||||
// if (pos == inp.size())
|
||||
// break;
|
||||
// inp = inp.substr(pos + 1);
|
||||
// }
|
||||
// return out;
|
||||
// };
|
||||
|
||||
ret[jss::TYPES]["Done"] = -1;
|
||||
std::map<int32_t, std::string> type_map{{-1, "Done"}};
|
||||
for (auto [value, name] : magic_enum::enum_entries<SerializedTypeID>())
|
||||
{
|
||||
std::string type_name =
|
||||
translate(STR(name).substr(4) /* remove STI_ */);
|
||||
int32_t type_value = std::stoi(STR(value));
|
||||
ret[jss::TYPES][type_name] = type_value;
|
||||
type_map[type_value] = type_name;
|
||||
}
|
||||
// ret[jss::TYPES]["Done"] = -1;
|
||||
// std::map<int32_t, std::string> type_map{{-1, "Done"}};
|
||||
// for (auto [value, name] : magic_enum::enum_entries<SerializedTypeID>())
|
||||
// {
|
||||
// std::string type_name =
|
||||
// translate(STR(name).substr(4) /* remove STI_ */);
|
||||
// int32_t type_value = std::stoi(STR(value));
|
||||
// ret[jss::TYPES][type_name] = type_value;
|
||||
// type_map[type_value] = type_name;
|
||||
// }
|
||||
|
||||
ret[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
|
||||
ret[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
|
||||
// ret[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
|
||||
// ret[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
|
||||
|
||||
for (auto [value, name] : magic_enum::enum_entries<LedgerEntryType>())
|
||||
ret[jss::LEDGER_ENTRY_TYPES]
|
||||
[translate(STR(name).substr(2) /* remove lt_ */)] =
|
||||
std::stoi(STR(value));
|
||||
// for (auto [value, name] : magic_enum::enum_entries<LedgerEntryType>())
|
||||
// ret[jss::LEDGER_ENTRY_TYPES]
|
||||
// [translate(STR(name).substr(2) /* remove lt_ */)] =
|
||||
// std::stoi(STR(value));
|
||||
|
||||
ret[jss::FIELDS] = Json::arrayValue;
|
||||
// ret[jss::FIELDS] = Json::arrayValue;
|
||||
|
||||
uint32_t i = 0;
|
||||
{
|
||||
Json::Value a = Json::arrayValue;
|
||||
a[0U] = "Generic";
|
||||
Json::Value v = Json::objectValue;
|
||||
v[jss::nth] = 0;
|
||||
v[jss::isVLEncoded] = false;
|
||||
v[jss::isSerialized] = false;
|
||||
v[jss::isSigningField] = false;
|
||||
v[jss::type] = "Unknown";
|
||||
a[1U] = v;
|
||||
ret[jss::FIELDS][i++] = a;
|
||||
}
|
||||
// uint32_t i = 0;
|
||||
// {
|
||||
// Json::Value a = Json::arrayValue;
|
||||
// a[0U] = "Generic";
|
||||
// Json::Value v = Json::objectValue;
|
||||
// v[jss::nth] = 0;
|
||||
// v[jss::isVLEncoded] = false;
|
||||
// v[jss::isSerialized] = false;
|
||||
// v[jss::isSigningField] = false;
|
||||
// v[jss::type] = "Unknown";
|
||||
// a[1U] = v;
|
||||
// ret[jss::FIELDS][i++] = a;
|
||||
// }
|
||||
|
||||
{
|
||||
Json::Value a = Json::arrayValue;
|
||||
a[0U] = "Invalid";
|
||||
Json::Value v = Json::objectValue;
|
||||
v[jss::nth] = -1;
|
||||
v[jss::isVLEncoded] = false;
|
||||
v[jss::isSerialized] = false;
|
||||
v[jss::isSigningField] = false;
|
||||
v[jss::type] = "Unknown";
|
||||
a[1U] = v;
|
||||
ret[jss::FIELDS][i++] = a;
|
||||
}
|
||||
// {
|
||||
// Json::Value a = Json::arrayValue;
|
||||
// a[0U] = "Invalid";
|
||||
// Json::Value v = Json::objectValue;
|
||||
// v[jss::nth] = -1;
|
||||
// v[jss::isVLEncoded] = false;
|
||||
// v[jss::isSerialized] = false;
|
||||
// v[jss::isSigningField] = false;
|
||||
// v[jss::type] = "Unknown";
|
||||
// a[1U] = v;
|
||||
// ret[jss::FIELDS][i++] = a;
|
||||
// }
|
||||
|
||||
{
|
||||
Json::Value a = Json::arrayValue;
|
||||
a[0U] = "ObjectEndMarker";
|
||||
Json::Value v = Json::objectValue;
|
||||
v[jss::nth] = 1;
|
||||
v[jss::isVLEncoded] = false;
|
||||
v[jss::isSerialized] = false;
|
||||
v[jss::isSigningField] = true;
|
||||
v[jss::type] = "STObject";
|
||||
a[1U] = v;
|
||||
ret[jss::FIELDS][i++] = a;
|
||||
}
|
||||
// {
|
||||
// Json::Value a = Json::arrayValue;
|
||||
// a[0U] = "ObjectEndMarker";
|
||||
// Json::Value v = Json::objectValue;
|
||||
// v[jss::nth] = 1;
|
||||
// v[jss::isVLEncoded] = false;
|
||||
// v[jss::isSerialized] = false;
|
||||
// v[jss::isSigningField] = true;
|
||||
// v[jss::type] = "STObject";
|
||||
// a[1U] = v;
|
||||
// ret[jss::FIELDS][i++] = a;
|
||||
// }
|
||||
|
||||
{
|
||||
Json::Value a = Json::arrayValue;
|
||||
a[0U] = "ArrayEndMarker";
|
||||
Json::Value v = Json::objectValue;
|
||||
v[jss::nth] = 1;
|
||||
v[jss::isVLEncoded] = false;
|
||||
v[jss::isSerialized] = false;
|
||||
v[jss::isSigningField] = true;
|
||||
v[jss::type] = "STArray";
|
||||
a[1U] = v;
|
||||
ret[jss::FIELDS][i++] = a;
|
||||
}
|
||||
// {
|
||||
// Json::Value a = Json::arrayValue;
|
||||
// a[0U] = "ArrayEndMarker";
|
||||
// Json::Value v = Json::objectValue;
|
||||
// v[jss::nth] = 1;
|
||||
// v[jss::isVLEncoded] = false;
|
||||
// v[jss::isSerialized] = false;
|
||||
// v[jss::isSigningField] = true;
|
||||
// v[jss::type] = "STArray";
|
||||
// a[1U] = v;
|
||||
// ret[jss::FIELDS][i++] = a;
|
||||
// }
|
||||
|
||||
{
|
||||
Json::Value a = Json::arrayValue;
|
||||
a[0U] = "hash";
|
||||
Json::Value v = Json::objectValue;
|
||||
v[jss::nth] = 257;
|
||||
v[jss::isVLEncoded] = false;
|
||||
v[jss::isSerialized] = false;
|
||||
v[jss::isSigningField] = false;
|
||||
v[jss::type] = "Hash256";
|
||||
a[1U] = v;
|
||||
ret[jss::FIELDS][i++] = a;
|
||||
}
|
||||
// {
|
||||
// Json::Value a = Json::arrayValue;
|
||||
// a[0U] = "hash";
|
||||
// Json::Value v = Json::objectValue;
|
||||
// v[jss::nth] = 257;
|
||||
// v[jss::isVLEncoded] = false;
|
||||
// v[jss::isSerialized] = false;
|
||||
// v[jss::isSigningField] = false;
|
||||
// v[jss::type] = "Hash256";
|
||||
// a[1U] = v;
|
||||
// ret[jss::FIELDS][i++] = a;
|
||||
// }
|
||||
|
||||
{
|
||||
Json::Value a = Json::arrayValue;
|
||||
a[0U] = "index";
|
||||
Json::Value v = Json::objectValue;
|
||||
v[jss::nth] = 258;
|
||||
v[jss::isVLEncoded] = false;
|
||||
v[jss::isSerialized] = false;
|
||||
v[jss::isSigningField] = false;
|
||||
v[jss::type] = "Hash256";
|
||||
a[1U] = v;
|
||||
ret[jss::FIELDS][i++] = a;
|
||||
}
|
||||
// {
|
||||
// Json::Value a = Json::arrayValue;
|
||||
// a[0U] = "index";
|
||||
// Json::Value v = Json::objectValue;
|
||||
// v[jss::nth] = 258;
|
||||
// v[jss::isVLEncoded] = false;
|
||||
// v[jss::isSerialized] = false;
|
||||
// v[jss::isSigningField] = false;
|
||||
// v[jss::type] = "Hash256";
|
||||
// a[1U] = v;
|
||||
// ret[jss::FIELDS][i++] = a;
|
||||
// }
|
||||
|
||||
{
|
||||
Json::Value a = Json::arrayValue;
|
||||
a[0U] = "taker_gets_funded";
|
||||
Json::Value v = Json::objectValue;
|
||||
v[jss::nth] = 258;
|
||||
v[jss::isVLEncoded] = false;
|
||||
v[jss::isSerialized] = false;
|
||||
v[jss::isSigningField] = false;
|
||||
v[jss::type] = "Amount";
|
||||
a[1U] = v;
|
||||
ret[jss::FIELDS][i++] = a;
|
||||
}
|
||||
// {
|
||||
// Json::Value a = Json::arrayValue;
|
||||
// a[0U] = "taker_gets_funded";
|
||||
// Json::Value v = Json::objectValue;
|
||||
// v[jss::nth] = 258;
|
||||
// v[jss::isVLEncoded] = false;
|
||||
// v[jss::isSerialized] = false;
|
||||
// v[jss::isSigningField] = false;
|
||||
// v[jss::type] = "Amount";
|
||||
// a[1U] = v;
|
||||
// ret[jss::FIELDS][i++] = a;
|
||||
// }
|
||||
|
||||
{
|
||||
Json::Value a = Json::arrayValue;
|
||||
a[0U] = "taker_pays_funded";
|
||||
Json::Value v = Json::objectValue;
|
||||
v[jss::nth] = 259;
|
||||
v[jss::isVLEncoded] = false;
|
||||
v[jss::isSerialized] = false;
|
||||
v[jss::isSigningField] = false;
|
||||
v[jss::type] = "Amount";
|
||||
a[1U] = v;
|
||||
ret[jss::FIELDS][i++] = a;
|
||||
}
|
||||
// {
|
||||
// Json::Value a = Json::arrayValue;
|
||||
// a[0U] = "taker_pays_funded";
|
||||
// Json::Value v = Json::objectValue;
|
||||
// v[jss::nth] = 259;
|
||||
// v[jss::isVLEncoded] = false;
|
||||
// v[jss::isSerialized] = false;
|
||||
// v[jss::isSigningField] = false;
|
||||
// v[jss::type] = "Amount";
|
||||
// a[1U] = v;
|
||||
// ret[jss::FIELDS][i++] = a;
|
||||
// }
|
||||
|
||||
for (auto const& [code, f] : ripple::SField::knownCodeToField)
|
||||
{
|
||||
if (f->fieldName == "")
|
||||
continue;
|
||||
// for (auto const& [code, f] : ripple::SField::knownCodeToField)
|
||||
// {
|
||||
// if (f->fieldName == "")
|
||||
// continue;
|
||||
|
||||
Json::Value innerObj = Json::objectValue;
|
||||
// Json::Value innerObj = Json::objectValue;
|
||||
|
||||
uint32_t fc = code & 0xFFU;
|
||||
uint32_t tc = code >> 16U;
|
||||
// uint32_t fc = code & 0xFFU;
|
||||
// uint32_t tc = code >> 16U;
|
||||
|
||||
innerObj[jss::nth] = fc;
|
||||
// innerObj[jss::nth] = fc;
|
||||
|
||||
innerObj[jss::isVLEncoded] =
|
||||
(tc == 7U /* Blob */ || tc == 8U /* AccountID */ ||
|
||||
tc == 19U /* Vector256 */);
|
||||
// innerObj[jss::isVLEncoded] =
|
||||
// (tc == 7U /* Blob */ || tc == 8U /* AccountID */ ||
|
||||
// tc == 19U /* Vector256 */);
|
||||
|
||||
innerObj[jss::isSerialized] =
|
||||
(tc <
|
||||
10000); /* TRANSACTION, LEDGER_ENTRY, VALIDATION, METADATA */
|
||||
// innerObj[jss::isSerialized] =
|
||||
// (tc <
|
||||
// 10000); /* TRANSACTION, LEDGER_ENTRY, VALIDATION, METADATA */
|
||||
|
||||
innerObj[jss::isSigningField] = f->shouldInclude(false);
|
||||
// innerObj[jss::isSigningField] = f->shouldInclude(false);
|
||||
|
||||
innerObj[jss::type] = type_map[tc];
|
||||
// innerObj[jss::type] = type_map[tc];
|
||||
|
||||
Json::Value innerArray = Json::arrayValue;
|
||||
innerArray[0U] = f->fieldName;
|
||||
innerArray[1U] = innerObj;
|
||||
// Json::Value innerArray = Json::arrayValue;
|
||||
// innerArray[0U] = f->fieldName;
|
||||
// innerArray[1U] = innerObj;
|
||||
|
||||
ret[jss::FIELDS][i++] = innerArray;
|
||||
}
|
||||
// ret[jss::FIELDS][i++] = innerArray;
|
||||
// }
|
||||
|
||||
ret[jss::TRANSACTION_RESULTS] = Json::objectValue;
|
||||
for (auto [value, name] : magic_enum::enum_entries<TELcodes>())
|
||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
for (auto [value, name] : magic_enum::enum_entries<TEMcodes>())
|
||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
for (auto [value, name] : magic_enum::enum_entries<TEFcodes>())
|
||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
for (auto [value, name] : magic_enum::enum_entries<TERcodes>())
|
||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
for (auto [value, name] : magic_enum::enum_entries<TEScodes>())
|
||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
for (auto [value, name] : magic_enum::enum_entries<TECcodes>())
|
||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
// ret[jss::TRANSACTION_RESULTS] = Json::objectValue;
|
||||
// for (auto [value, name] : magic_enum::enum_entries<TELcodes>())
|
||||
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
// for (auto [value, name] : magic_enum::enum_entries<TEMcodes>())
|
||||
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
// for (auto [value, name] : magic_enum::enum_entries<TEFcodes>())
|
||||
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
// for (auto [value, name] : magic_enum::enum_entries<TERcodes>())
|
||||
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
// for (auto [value, name] : magic_enum::enum_entries<TEScodes>())
|
||||
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
// for (auto [value, name] : magic_enum::enum_entries<TECcodes>())
|
||||
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||
|
||||
|
||||
auto const translate_tt = [](std::string inp) -> std::string {
|
||||
if (inp == "PaychanClaim")
|
||||
return "PaymentChannelClaim";
|
||||
if (inp == "PaychanCreate")
|
||||
return "PaymentChannelCreate";
|
||||
if (inp == "PaychanFund")
|
||||
return "PaymentChannelFund";
|
||||
if (inp == "RegularKeySet")
|
||||
return "SetRegularKey";
|
||||
if (inp == "HookSet")
|
||||
return "SetHook";
|
||||
return inp;
|
||||
};
|
||||
// auto const translate_tt = [](std::string inp) -> std::string {
|
||||
// if (inp == "PaychanClaim")
|
||||
// return "PaymentChannelClaim";
|
||||
// if (inp == "PaychanCreate")
|
||||
// return "PaymentChannelCreate";
|
||||
// if (inp == "PaychanFund")
|
||||
// return "PaymentChannelFund";
|
||||
// if (inp == "RegularKeySet")
|
||||
// return "SetRegularKey";
|
||||
// if (inp == "HookSet")
|
||||
// return "SetHook";
|
||||
// return inp;
|
||||
// };
|
||||
|
||||
ret[jss::TRANSACTION_TYPES] = Json::objectValue;
|
||||
ret[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
|
||||
for (auto [value, name] : magic_enum::enum_entries<TxType>())
|
||||
ret[jss::TRANSACTION_TYPES][translate_tt(translate(STR(name).substr(2)))] =
|
||||
std::stoi(STR(value));
|
||||
// ret[jss::TRANSACTION_TYPES] = Json::objectValue;
|
||||
// ret[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
|
||||
// for (auto [value, name] : magic_enum::enum_entries<TxType>())
|
||||
// ret[jss::TRANSACTION_TYPES][translate_tt(translate(STR(name).substr(2)))] =
|
||||
// std::stoi(STR(value));
|
||||
|
||||
// generate hash
|
||||
{
|
||||
const std::string out = Json::FastWriter().write(ret);
|
||||
defsHash =
|
||||
ripple::sha512Half(ripple::Slice{out.data(), out.size()});
|
||||
ret[jss::hash] = to_string(*defsHash);
|
||||
}
|
||||
// // generate hash
|
||||
// {
|
||||
// const std::string out = Json::FastWriter().write(ret);
|
||||
// defsHash =
|
||||
// ripple::sha512Half(ripple::Slice{out.data(), out.size()});
|
||||
// ret[jss::hash] = to_string(*defsHash);
|
||||
// }
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
2648
src/test/app/Import_test.cpp
Normal file
2648
src/test/app/Import_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -129,7 +129,7 @@ public:
|
||||
jvn[jss::Fee] = to_string(env.current()->fees().base);
|
||||
jvn[jss::Sequence] = env.seq(alice);
|
||||
jvn[jss::LastLedgerSequence] = env.current()->info().seq + 2;
|
||||
auto jt = env.jtnofill(jvn);
|
||||
auto jt = env.jtnofill(jvn, alice);
|
||||
Serializer s;
|
||||
jt.stx->add(s);
|
||||
BEAST_EXPECT(env.rpc("submit", strHex(s.slice()))[jss::result][jss::engine_result] == "telREQUIRES_NETWORK_ID");
|
||||
|
||||
@@ -448,11 +448,23 @@ public:
|
||||
/** Create a JTx from parameters. */
|
||||
template <class JsonValue, class... FN>
|
||||
JTx
|
||||
jtnofill(JsonValue&& jv, FN const&... fN)
|
||||
jt(JsonValue&& jv, Account account, FN const&... fN)
|
||||
{
|
||||
JTx jt(std::forward<JsonValue>(jv));
|
||||
invoke(jt, fN...);
|
||||
autofill_sig(jt);
|
||||
acct_autofill(jt, account);
|
||||
jt.stx = st(jt);
|
||||
return jt;
|
||||
}
|
||||
|
||||
/** Create a JTx from parameters. */
|
||||
template <class JsonValue, class Account, class... FN>
|
||||
JTx
|
||||
jtnofill(JsonValue&& jv, Account account, FN const&... fN)
|
||||
{
|
||||
JTx jt(std::forward<JsonValue>(jv));
|
||||
invoke(jt, fN...);
|
||||
autofill_sig(jt, account);
|
||||
jt.stx = st(jt);
|
||||
return jt;
|
||||
}
|
||||
@@ -518,6 +530,22 @@ public:
|
||||
{
|
||||
apply(std::forward<JsonValue>(jv), fN...);
|
||||
}
|
||||
|
||||
/** Apply funclets and submit. */
|
||||
/** @{ */
|
||||
template <class JsonValue, class Account, class... FN>
|
||||
void
|
||||
apply(JsonValue&& jv, Account const& account = {}, FN const&... fN)
|
||||
{
|
||||
submit(jt(std::forward<JsonValue>(jv), account, fN...));
|
||||
}
|
||||
|
||||
template <class JsonValue, class Account, class... FN>
|
||||
void
|
||||
operator()(JsonValue&& jv, Account const& account, FN const&... fN)
|
||||
{
|
||||
apply(std::forward<JsonValue>(jv), account, fN...);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Return the TER for the last JTx. */
|
||||
@@ -662,7 +690,10 @@ protected:
|
||||
std::unordered_map<std::string, std::string> const& headers = {});
|
||||
|
||||
void
|
||||
autofill_sig(JTx& jt);
|
||||
autofill_sig(JTx& jt, Account const& account);
|
||||
|
||||
virtual void
|
||||
acct_autofill(JTx& jt, Account const& account);
|
||||
|
||||
virtual void
|
||||
autofill(JTx& jt);
|
||||
|
||||
@@ -402,14 +402,14 @@ Env::txid() const
|
||||
}
|
||||
|
||||
void
|
||||
Env::autofill_sig(JTx& jt)
|
||||
Env::autofill_sig(JTx& jt, Account const& account)
|
||||
{
|
||||
auto& jv = jt.jv;
|
||||
if (jt.signer)
|
||||
return jt.signer(*this, jt);
|
||||
if (!jt.fill_sig)
|
||||
return;
|
||||
auto const account = lookup(jv[jss::Account].asString());
|
||||
|
||||
if (!app().checkSigs())
|
||||
{
|
||||
jv[jss::SigningPubKey] = strHex(account.pk().slice());
|
||||
@@ -424,6 +424,31 @@ Env::autofill_sig(JTx& jt)
|
||||
jtx::sign(jv, account);
|
||||
}
|
||||
|
||||
void
|
||||
Env::acct_autofill(JTx& jt, Account const& account)
|
||||
{
|
||||
auto& jv = jt.jv;
|
||||
if (jt.fill_fee)
|
||||
jtx::fill_fee(jv, *current());
|
||||
if (jt.fill_seq)
|
||||
jtx::fill_seq(jv, *current());
|
||||
|
||||
uint32_t networkID = app().config().NETWORK_ID;
|
||||
if (!jv.isMember(jss::NetworkID) && networkID > 1024)
|
||||
jv[jss::NetworkID] = std::to_string(networkID);
|
||||
|
||||
// Must come last
|
||||
try
|
||||
{
|
||||
autofill_sig(jt, account);
|
||||
}
|
||||
catch (parse_error const&)
|
||||
{
|
||||
test.log << "parse failed:\n" << pretty(jv) << std::endl;
|
||||
Rethrow();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Env::autofill(JTx& jt)
|
||||
{
|
||||
@@ -432,15 +457,16 @@ Env::autofill(JTx& jt)
|
||||
jtx::fill_fee(jv, *current());
|
||||
if (jt.fill_seq)
|
||||
jtx::fill_seq(jv, *current());
|
||||
|
||||
|
||||
uint32_t networkID = app().config().NETWORK_ID;
|
||||
if (!jv.isMember(jss::NetworkID) && networkID > 1024)
|
||||
jv[jss::NetworkID] = std::to_string(networkID);
|
||||
|
||||
|
||||
// Must come last
|
||||
try
|
||||
{
|
||||
autofill_sig(jt);
|
||||
auto const account = lookup(jv[jss::Account].asString());
|
||||
autofill_sig(jt, account);
|
||||
}
|
||||
catch (parse_error const&)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user