mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-20 02:25:53 +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/Flow_test.cpp
|
||||||
src/test/app/Freeze_test.cpp
|
src/test/app/Freeze_test.cpp
|
||||||
src/test/app/HashRouter_test.cpp
|
src/test/app/HashRouter_test.cpp
|
||||||
|
src/test/app/Import_test.cpp
|
||||||
src/test/app/LedgerHistory_test.cpp
|
src/test/app/LedgerHistory_test.cpp
|
||||||
src/test/app/LedgerLoad_test.cpp
|
src/test/app/LedgerLoad_test.cpp
|
||||||
src/test/app/LedgerMaster_test.cpp
|
src/test/app/LedgerMaster_test.cpp
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
NIH dep: wasmedge: web assembly runtime for hooks.
|
NIH dep: wasmedge: web assembly runtime for hooks.
|
||||||
#]===================================================================]
|
#]===================================================================]
|
||||||
|
|
||||||
|
find_package(Curses REQUIRED)
|
||||||
|
include_directories(${CURSES_INCLUDE_DIR})
|
||||||
find_package(LLVM REQUIRED CONFIG)
|
find_package(LLVM REQUIRED CONFIG)
|
||||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||||
@@ -24,6 +26,7 @@ ExternalProject_Add (wasmedge_src
|
|||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||||
-DLLVM_DIR=${LLVM_DIR}
|
-DLLVM_DIR=${LLVM_DIR}
|
||||||
-DLLVM_LIBRARY_DIR=${LLVM_LIBRARY_DIR}
|
-DLLVM_LIBRARY_DIR=${LLVM_LIBRARY_DIR}
|
||||||
|
-DLLVM_ENABLE_TERMINFO=OFF
|
||||||
$<$<NOT:$<BOOL:${is_multiconfig}>>:-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}>
|
$<$<NOT:$<BOOL:${is_multiconfig}>>:-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}>
|
||||||
$<$<BOOL:${MSVC}>:
|
$<$<BOOL:${MSVC}>:
|
||||||
"-DCMAKE_C_FLAGS=-GR -Gd -fp:precise -FS -MP -march=native"
|
"-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/"
|
"${wasmedge_src_BINARY_DIR}/include/api/"
|
||||||
)
|
)
|
||||||
target_link_libraries (ripple_libs INTERFACE wasmedge)
|
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)
|
add_library (NIH::WasmEdge ALIAS wasmedge)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <ripple/protocol/Import.h>
|
||||||
#include <ripple/app/tx/impl/Import.h>
|
#include <ripple/app/tx/impl/Import.h>
|
||||||
#include <ripple/basics/Log.h>
|
#include <ripple/basics/Log.h>
|
||||||
#include <ripple/ledger/View.h>
|
#include <ripple/ledger/View.h>
|
||||||
@@ -61,412 +62,6 @@ Import::makeTxConsequences(PreflightContext const& ctx)
|
|||||||
return TxConsequences{ctx.tx, calculate(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::pair<
|
||||||
std::unique_ptr<STTx const>, // txn
|
std::unique_ptr<STTx const>, // txn
|
||||||
@@ -535,6 +130,14 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
|
|
||||||
auto& tx = ctx.tx;
|
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))
|
if (tx.getFieldVL(sfBlob).size() > (512 * 1024))
|
||||||
{
|
{
|
||||||
JLOG(ctx.j.warn())
|
JLOG(ctx.j.warn())
|
||||||
@@ -554,7 +157,7 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse blob as json
|
// parse blob as json
|
||||||
auto const xpop =
|
auto const xpop =
|
||||||
syntaxCheckXPOP(tx.getFieldVL(sfBlob), ctx.j);
|
syntaxCheckXPOP(tx.getFieldVL(sfBlob), ctx.j);
|
||||||
|
|
||||||
if (!xpop)
|
if (!xpop)
|
||||||
@@ -778,13 +381,31 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(list.isMember(jss::sequence) && list[jss::sequence].isInt() &&
|
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()))
|
|
||||||
{
|
{
|
||||||
JLOG(ctx.j.warn())
|
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();
|
<< tx.getTransactionID();
|
||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
}
|
}
|
||||||
@@ -838,7 +459,7 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
if (!([](Json::Value const& proof, std::string hash) -> bool
|
if (!([](Json::Value const& proof, std::string hash) -> bool
|
||||||
{
|
{
|
||||||
auto const proofContains =
|
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)
|
if (depth > 32)
|
||||||
return false;
|
return false;
|
||||||
@@ -885,7 +506,7 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
([](Json::Value const& proof) -> uint256
|
([](Json::Value const& proof) -> uint256
|
||||||
{
|
{
|
||||||
auto hashProof =
|
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;
|
const uint256 nullhash;
|
||||||
|
|
||||||
@@ -918,6 +539,7 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
|
|
||||||
for (int x = 0; x < 16; ++x)
|
for (int x = 0; x < 16; ++x)
|
||||||
{
|
{
|
||||||
|
// Duplicate / Sanity
|
||||||
std::string const nibble (1, "0123456789ABCDEF"[x]);
|
std::string const nibble (1, "0123456789ABCDEF"[x]);
|
||||||
if (!proof[jss::children].isMember(nibble))
|
if (!proof[jss::children].isMember(nibble))
|
||||||
hash_append(h, nullhash);
|
hash_append(h, nullhash);
|
||||||
@@ -948,6 +570,7 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
auto coins = parse_uint64(lgr[jss::coins].asString());
|
auto coins = parse_uint64(lgr[jss::coins].asString());
|
||||||
uint256 phash, acroot;
|
uint256 phash, acroot;
|
||||||
|
|
||||||
|
// Duplicate / Sanity - syntaxCheckXPOP
|
||||||
if (!coins ||
|
if (!coins ||
|
||||||
!phash.parseHex(lgr[jss::phash].asString()) ||
|
!phash.parseHex(lgr[jss::phash].asString()) ||
|
||||||
!acroot.parseHex(lgr[jss::acroot].asString()))
|
!acroot.parseHex(lgr[jss::acroot].asString()))
|
||||||
@@ -1044,11 +667,18 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
validatorsMaster[nodemaster] = nodepub;
|
validatorsMaster[nodemaster] = nodepub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JLOG(ctx.j.trace())
|
||||||
|
<< "totalValidatorCount: " << totalValidatorCount;
|
||||||
|
|
||||||
uint64_t quorum = totalValidatorCount * 0.8;
|
uint64_t quorum = totalValidatorCount * 0.8;
|
||||||
|
|
||||||
if (quorum == 0)
|
if (quorum == 0)
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.warn())
|
||||||
|
<< "Import: resetting quorum to 1. "
|
||||||
|
<< tx.getTransactionID();
|
||||||
quorum = 1;
|
quorum = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// count how many validations this ledger hash has
|
// 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
|
// if it is less than the Account Sequence in the xpop then mint and
|
||||||
// update sfImportSequence
|
// update sfImportSequence
|
||||||
|
|
||||||
|
// Duplicate / Sanity
|
||||||
if (!stpTrans->isFieldPresent(sfSequence) ||
|
if (!stpTrans->isFieldPresent(sfSequence) ||
|
||||||
!stpTrans->isFieldPresent(sfFee) ||
|
!stpTrans->isFieldPresent(sfFee) ||
|
||||||
!isXRP(stpTrans->getFieldAmount(sfFee)))
|
!isXRP(stpTrans->getFieldAmount(sfFee)))
|
||||||
@@ -1195,13 +825,11 @@ Import::preflight(PreflightContext const& ctx)
|
|||||||
<< tx.getTransactionID();
|
<< tx.getTransactionID();
|
||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return preflight2(ctx);
|
return preflight2(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// RH TODO: manifest serials should be kept on chain
|
// RH TODO: manifest serials should be kept on chain
|
||||||
|
|
||||||
|
|
||||||
TER
|
TER
|
||||||
Import::preclaim(PreclaimContext const& ctx)
|
Import::preclaim(PreclaimContext const& ctx)
|
||||||
{
|
{
|
||||||
@@ -1238,14 +866,14 @@ Import::preclaim(PreclaimContext const& ctx)
|
|||||||
return tefPAST_IMPORT_SEQ;
|
return tefPAST_IMPORT_SEQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const vl = getVLInfo(*xpop, ctx.j);
|
auto const vlInfo = getVLInfo(*xpop, ctx.j);
|
||||||
|
|
||||||
if (!vl)
|
if (!vlInfo)
|
||||||
return tefINTERNAL;
|
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 tefPAST_IMPORT_VL_SEQ;
|
||||||
|
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
@@ -1282,6 +910,7 @@ Import::doApply()
|
|||||||
auto const id = ctx_.tx[sfAccount];
|
auto const id = ctx_.tx[sfAccount];
|
||||||
|
|
||||||
auto const k = keylet::account(id);
|
auto const k = keylet::account(id);
|
||||||
|
auto const ksl = keylet::signers(id);
|
||||||
|
|
||||||
auto sle = view().peek(k);
|
auto sle = view().peek(k);
|
||||||
|
|
||||||
@@ -1292,17 +921,18 @@ Import::doApply()
|
|||||||
|
|
||||||
auto const tt = stpTrans->getTxnType();
|
auto const tt = stpTrans->getTxnType();
|
||||||
|
|
||||||
|
|
||||||
// rekeying is only allowed on a tesSUCCESS, but minting is allowed on any tes or tec code.
|
// rekeying is only allowed on a tesSUCCESS, but minting is allowed on any tes or tec code.
|
||||||
if (meta->getFieldU8(sfTransactionResult) == tesSUCCESS)
|
if (meta->getFieldU8(sfTransactionResult) == tesSUCCESS)
|
||||||
{
|
{
|
||||||
if (tt == ttSIGNER_LIST_SET)
|
if (tt == ttSIGNER_LIST_SET)
|
||||||
{
|
{
|
||||||
|
JLOG(ctx_.journal.warn()) << "SingerListSet";
|
||||||
// key import: signer list
|
// key import: signer list
|
||||||
setSignerEntries = stpTrans->getFieldArray(sfSignerEntries);
|
setSignerEntries = stpTrans->getFieldArray(sfSignerEntries);
|
||||||
}
|
}
|
||||||
else if (tt == ttREGULAR_KEY_SET)
|
else if (tt == ttREGULAR_KEY_SET)
|
||||||
{
|
{
|
||||||
|
JLOG(ctx_.journal.warn()) << "SetRegularKey";
|
||||||
// key import: regular key
|
// key import: regular key
|
||||||
setRegularKey = stpTrans->getAccountID(sfRegularKey);
|
setRegularKey = stpTrans->getAccountID(sfRegularKey);
|
||||||
}
|
}
|
||||||
@@ -1310,9 +940,10 @@ Import::doApply()
|
|||||||
|
|
||||||
bool const create = !sle;
|
bool const create = !sle;
|
||||||
|
|
||||||
if (!sle)
|
if (create)
|
||||||
{
|
{
|
||||||
// Create the account.
|
// Create the account.
|
||||||
|
JLOG(ctx_.journal.warn()) << "create - create account";
|
||||||
std::uint32_t const seqno{
|
std::uint32_t const seqno{
|
||||||
view().rules().enabled(featureDeletableAccounts) ? view().seq()
|
view().rules().enabled(featureDeletableAccounts) ? view().seq()
|
||||||
: 1};
|
: 1};
|
||||||
@@ -1332,6 +963,8 @@ Import::doApply()
|
|||||||
return tefINTERNAL;
|
return tefINTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JLOG(ctx_.journal.warn()) << "Import: inital balance" << initBal;
|
||||||
|
|
||||||
sle->setFieldAmount(sfBalance, initBal);
|
sle->setFieldAmount(sfBalance, initBal);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1343,16 +976,25 @@ Import::doApply()
|
|||||||
return tefINTERNAL;
|
return tefINTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setSignerEntries)
|
if (setSignerEntries) {
|
||||||
sle->setFieldArray(sfSignerEntries, *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)
|
else if (setRegularKey)
|
||||||
|
{
|
||||||
|
JLOG(ctx_.journal.warn()) << "set regular key";
|
||||||
sle->setAccountID(sfRegularKey, *setRegularKey);
|
sle->setAccountID(sfRegularKey, *setRegularKey);
|
||||||
|
}
|
||||||
|
|
||||||
if (create)
|
if (create)
|
||||||
{
|
{
|
||||||
|
JLOG(ctx_.journal.warn()) << "create";
|
||||||
if (!signedWithMaster)
|
if (!signedWithMaster)
|
||||||
{
|
{
|
||||||
// disable master if the account is created using non-master key
|
// disable master if the account is created using non-master key
|
||||||
|
JLOG(ctx_.journal.warn()) << "create - disable master";
|
||||||
sle->setAccountID(sfRegularKey, noAccount());
|
sle->setAccountID(sfRegularKey, noAccount());
|
||||||
sle->setFieldU32(sfFlags, lsfDisableMaster);
|
sle->setFieldU32(sfFlags, lsfDisableMaster);
|
||||||
}
|
}
|
||||||
@@ -1361,11 +1003,13 @@ Import::doApply()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// account already exists
|
// account already exists
|
||||||
|
JLOG(ctx_.journal.warn()) << "update - import sequence";
|
||||||
sle->setFieldU32(sfImportSequence, importSequence);
|
sle->setFieldU32(sfImportSequence, importSequence);
|
||||||
|
|
||||||
// credit the PoB
|
// credit the PoB
|
||||||
if (burn > beast::zero)
|
if (burn > beast::zero)
|
||||||
{
|
{
|
||||||
|
JLOG(ctx_.journal.warn()) << "update - credit burn";
|
||||||
STAmount startBal = sle->getFieldAmount(sfBalance);
|
STAmount startBal = sle->getFieldAmount(sfBalance);
|
||||||
STAmount finalBal = startBal + burn;
|
STAmount finalBal = startBal + burn;
|
||||||
if (finalBal > startBal)
|
if (finalBal > startBal)
|
||||||
@@ -1388,6 +1032,7 @@ Import::doApply()
|
|||||||
if (!sleVL)
|
if (!sleVL)
|
||||||
{
|
{
|
||||||
// create VL import seq counter
|
// create VL import seq counter
|
||||||
|
JLOG(ctx_.journal.warn()) << "create vl seq - insert import sequence + public key";
|
||||||
sleVL = std::make_shared<SLE>(keyletVL);
|
sleVL = std::make_shared<SLE>(keyletVL);
|
||||||
sleVL->setFieldU32(sfImportSequence, infoVL->first);
|
sleVL->setFieldU32(sfImportSequence, infoVL->first);
|
||||||
sleVL->setFieldVL(sfPublicKey, infoVL->second.slice());
|
sleVL->setFieldVL(sfPublicKey, infoVL->second.slice());
|
||||||
@@ -1395,6 +1040,7 @@ Import::doApply()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
JLOG(ctx_.journal.warn()) << "update vl - insert import sequence";
|
||||||
uint32_t current = sleVL->getFieldU32(sfImportSequence);
|
uint32_t current = sleVL->getFieldU32(sfImportSequence);
|
||||||
|
|
||||||
if (current > infoVL->first)
|
if (current > infoVL->first)
|
||||||
@@ -1421,6 +1067,7 @@ XRPAmount
|
|||||||
Import::calculateBaseFee(ReadView const& view, STTx const& tx)
|
Import::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||||
{
|
{
|
||||||
return XRPAmount { 0 } ;
|
return XRPAmount { 0 } ;
|
||||||
|
// return Transactor::calculateBaseFee(view, tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -167,10 +167,10 @@ XRPNotCreated::finalize(
|
|||||||
dropsAdded += Import::INITIAL_IMPORT_XRP; // welcome amount for new imports
|
dropsAdded += Import::INITIAL_IMPORT_XRP; // welcome amount for new imports
|
||||||
|
|
||||||
JLOG(j.trace())
|
JLOG(j.trace())
|
||||||
<< "Invariant XRPNotCreated Import: "
|
<< "Invariant XRPNotCreated Import: "
|
||||||
<< "dropsAdded: " << dropsAdded
|
<< "dropsAdded: " << dropsAdded
|
||||||
<< " fee.drops(): " << fee.drops()
|
<< " fee.drops(): " << fee.drops()
|
||||||
<< " drops_: " << drops_
|
<< " drops_: " << drops_
|
||||||
<< " dropsAdded - fee.drops(): " << dropsAdded - fee.drops();
|
<< " dropsAdded - fee.drops(): " << dropsAdded - fee.drops();
|
||||||
|
|
||||||
return (drops_ == dropsAdded.drops() - fee.drops());
|
return (drops_ == dropsAdded.drops() - fee.drops());
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ Invoke::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
XRPAmount extraFee{0};
|
XRPAmount extraFee{0};
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfBlob))
|
if (tx.isFieldPresent(sfBlob))
|
||||||
extraFee += XRPAmount{ tx.getFieldVL(sfBlob).size() };
|
extraFee += XRPAmount{ static_cast<XRPAmount>(tx.getFieldVL(sfBlob).size()) };
|
||||||
|
|
||||||
if (tx.isFieldPresent(sfHookParameters))
|
if (tx.isFieldPresent(sfHookParameters))
|
||||||
{
|
{
|
||||||
@@ -93,7 +93,7 @@ Invoke::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
(param.isFieldPresent(sfHookParameterValue) ?
|
(param.isFieldPresent(sfHookParameterValue) ?
|
||||||
param.getFieldVL(sfHookParameterValue).size() : 0);
|
param.getFieldVL(sfHookParameterValue).size() : 0);
|
||||||
}
|
}
|
||||||
extraFee += XRPAmount { paramBytes };
|
extraFee += XRPAmount { static_cast<XRPAmount>(paramBytes) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return Transactor::calculateBaseFee(view, tx) + extraFee;
|
return Transactor::calculateBaseFee(view, tx) + extraFee;
|
||||||
|
|||||||
@@ -547,7 +547,7 @@ SetHook::calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
(param.isFieldPresent(sfHookParameterValue) ?
|
(param.isFieldPresent(sfHookParameterValue) ?
|
||||||
param.getFieldVL(sfHookParameterValue).size() : 0);
|
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(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(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(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(telCAN_NOT_QUEUE_IMPORT, "Import transaction was not able to be directly applied and cannot be queued."),
|
||||||
|
|
||||||
MAKE_ERROR(temMALFORMED, "Malformed transaction."),
|
MAKE_ERROR(temMALFORMED, "Malformed transaction."),
|
||||||
|
|||||||
@@ -69,281 +69,281 @@ private:
|
|||||||
Json::Value ret{Json::objectValue};
|
Json::Value ret{Json::objectValue};
|
||||||
ret[jss::TYPES] = Json::objectValue;
|
ret[jss::TYPES] = Json::objectValue;
|
||||||
|
|
||||||
auto const translate = [](std::string inp) -> std::string {
|
// auto const translate = [](std::string inp) -> std::string {
|
||||||
auto replace = [&](const char* f, const char* r) -> std::string {
|
// auto replace = [&](const char* f, const char* r) -> std::string {
|
||||||
std::string out = inp;
|
// std::string out = inp;
|
||||||
boost::replace_all(out, f, r);
|
// boost::replace_all(out, f, r);
|
||||||
return out;
|
// return out;
|
||||||
};
|
// };
|
||||||
|
|
||||||
auto find = [&](const char* s) -> bool {
|
// auto find = [&](const char* s) -> bool {
|
||||||
return inp.find(s) != std::string::npos;
|
// return inp.find(s) != std::string::npos;
|
||||||
};
|
// };
|
||||||
|
|
||||||
if (find("UINT"))
|
// if (find("UINT"))
|
||||||
{
|
// {
|
||||||
if (find("256") || find("160") || find("128"))
|
// if (find("256") || find("160") || find("128"))
|
||||||
return replace("UINT", "Hash");
|
// return replace("UINT", "Hash");
|
||||||
else
|
// else
|
||||||
return replace("UINT", "UInt");
|
// return replace("UINT", "UInt");
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (inp == "OBJECT")
|
// if (inp == "OBJECT")
|
||||||
return "STObject";
|
// return "STObject";
|
||||||
if (inp == "ARRAY")
|
// if (inp == "ARRAY")
|
||||||
return "STArray";
|
// return "STArray";
|
||||||
if (inp == "AMM")
|
// if (inp == "AMM")
|
||||||
return "AMM";
|
// return "AMM";
|
||||||
if (inp == "ACCOUNT")
|
// if (inp == "ACCOUNT")
|
||||||
return "AccountID";
|
// return "AccountID";
|
||||||
if (inp == "LEDGERENTRY")
|
// if (inp == "LEDGERENTRY")
|
||||||
return "LedgerEntry";
|
// return "LedgerEntry";
|
||||||
if (inp == "NOTPRESENT")
|
// if (inp == "NOTPRESENT")
|
||||||
return "NotPresent";
|
// return "NotPresent";
|
||||||
if (inp == "PATHSET")
|
// if (inp == "PATHSET")
|
||||||
return "PathSet";
|
// return "PathSet";
|
||||||
if (inp == "VL")
|
// if (inp == "VL")
|
||||||
return "Blob";
|
// return "Blob";
|
||||||
if (inp == "DIR_NODE")
|
// if (inp == "DIR_NODE")
|
||||||
return "DirectoryNode";
|
// return "DirectoryNode";
|
||||||
if (inp == "PAYCHAN")
|
// if (inp == "PAYCHAN")
|
||||||
return "PayChannel";
|
// return "PayChannel";
|
||||||
|
|
||||||
static const std::map<std::string, std::string>
|
// static const std::map<std::string, std::string>
|
||||||
capitalization_exceptions = {
|
// capitalization_exceptions = {
|
||||||
{"NFTOKEN", "NFToken"},
|
// {"NFTOKEN", "NFToken"},
|
||||||
{"UNL", "UNL"},
|
// {"UNL", "UNL"},
|
||||||
{"XCHAIN", "XChain"},
|
// {"XCHAIN", "XChain"},
|
||||||
{"ID", "ID"},
|
// {"ID", "ID"},
|
||||||
{"AMM", "AMM"},
|
// {"AMM", "AMM"},
|
||||||
{"URITOKEN", "URIToken"},
|
// {"URITOKEN", "URIToken"},
|
||||||
{"URI", "URI"}};
|
// {"URI", "URI"}};
|
||||||
|
|
||||||
std::string out;
|
// std::string out;
|
||||||
size_t pos = 0;
|
// size_t pos = 0;
|
||||||
for (;;)
|
// for (;;)
|
||||||
{
|
// {
|
||||||
pos = inp.find("_");
|
// pos = inp.find("_");
|
||||||
if (pos == std::string::npos)
|
// if (pos == std::string::npos)
|
||||||
pos = inp.size();
|
// pos = inp.size();
|
||||||
std::string token = inp.substr(0, pos);
|
// std::string token = inp.substr(0, pos);
|
||||||
if (auto const e = capitalization_exceptions.find(token);
|
// if (auto const e = capitalization_exceptions.find(token);
|
||||||
e != capitalization_exceptions.end())
|
// e != capitalization_exceptions.end())
|
||||||
out += e->second;
|
// out += e->second;
|
||||||
else if (token.size() > 1)
|
// else if (token.size() > 1)
|
||||||
{
|
// {
|
||||||
boost::algorithm::to_lower(token);
|
// boost::algorithm::to_lower(token);
|
||||||
token.data()[0] -= ('a' - 'A');
|
// token.data()[0] -= ('a' - 'A');
|
||||||
out += token;
|
// out += token;
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
out += token;
|
// out += token;
|
||||||
if (pos == inp.size())
|
// if (pos == inp.size())
|
||||||
break;
|
// break;
|
||||||
inp = inp.substr(pos + 1);
|
// inp = inp.substr(pos + 1);
|
||||||
}
|
// }
|
||||||
return out;
|
// return out;
|
||||||
};
|
// };
|
||||||
|
|
||||||
ret[jss::TYPES]["Done"] = -1;
|
// ret[jss::TYPES]["Done"] = -1;
|
||||||
std::map<int32_t, std::string> type_map{{-1, "Done"}};
|
// std::map<int32_t, std::string> type_map{{-1, "Done"}};
|
||||||
for (auto [value, name] : magic_enum::enum_entries<SerializedTypeID>())
|
// for (auto [value, name] : magic_enum::enum_entries<SerializedTypeID>())
|
||||||
{
|
// {
|
||||||
std::string type_name =
|
// std::string type_name =
|
||||||
translate(STR(name).substr(4) /* remove STI_ */);
|
// translate(STR(name).substr(4) /* remove STI_ */);
|
||||||
int32_t type_value = std::stoi(STR(value));
|
// int32_t type_value = std::stoi(STR(value));
|
||||||
ret[jss::TYPES][type_name] = type_value;
|
// ret[jss::TYPES][type_name] = type_value;
|
||||||
type_map[type_value] = type_name;
|
// type_map[type_value] = type_name;
|
||||||
}
|
// }
|
||||||
|
|
||||||
ret[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
|
// ret[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
|
||||||
ret[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
|
// ret[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
|
||||||
|
|
||||||
for (auto [value, name] : magic_enum::enum_entries<LedgerEntryType>())
|
// for (auto [value, name] : magic_enum::enum_entries<LedgerEntryType>())
|
||||||
ret[jss::LEDGER_ENTRY_TYPES]
|
// ret[jss::LEDGER_ENTRY_TYPES]
|
||||||
[translate(STR(name).substr(2) /* remove lt_ */)] =
|
// [translate(STR(name).substr(2) /* remove lt_ */)] =
|
||||||
std::stoi(STR(value));
|
// std::stoi(STR(value));
|
||||||
|
|
||||||
ret[jss::FIELDS] = Json::arrayValue;
|
// ret[jss::FIELDS] = Json::arrayValue;
|
||||||
|
|
||||||
uint32_t i = 0;
|
// uint32_t i = 0;
|
||||||
{
|
// {
|
||||||
Json::Value a = Json::arrayValue;
|
// Json::Value a = Json::arrayValue;
|
||||||
a[0U] = "Generic";
|
// a[0U] = "Generic";
|
||||||
Json::Value v = Json::objectValue;
|
// Json::Value v = Json::objectValue;
|
||||||
v[jss::nth] = 0;
|
// v[jss::nth] = 0;
|
||||||
v[jss::isVLEncoded] = false;
|
// v[jss::isVLEncoded] = false;
|
||||||
v[jss::isSerialized] = false;
|
// v[jss::isSerialized] = false;
|
||||||
v[jss::isSigningField] = false;
|
// v[jss::isSigningField] = false;
|
||||||
v[jss::type] = "Unknown";
|
// v[jss::type] = "Unknown";
|
||||||
a[1U] = v;
|
// a[1U] = v;
|
||||||
ret[jss::FIELDS][i++] = a;
|
// ret[jss::FIELDS][i++] = a;
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Json::Value a = Json::arrayValue;
|
// Json::Value a = Json::arrayValue;
|
||||||
a[0U] = "Invalid";
|
// a[0U] = "Invalid";
|
||||||
Json::Value v = Json::objectValue;
|
// Json::Value v = Json::objectValue;
|
||||||
v[jss::nth] = -1;
|
// v[jss::nth] = -1;
|
||||||
v[jss::isVLEncoded] = false;
|
// v[jss::isVLEncoded] = false;
|
||||||
v[jss::isSerialized] = false;
|
// v[jss::isSerialized] = false;
|
||||||
v[jss::isSigningField] = false;
|
// v[jss::isSigningField] = false;
|
||||||
v[jss::type] = "Unknown";
|
// v[jss::type] = "Unknown";
|
||||||
a[1U] = v;
|
// a[1U] = v;
|
||||||
ret[jss::FIELDS][i++] = a;
|
// ret[jss::FIELDS][i++] = a;
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Json::Value a = Json::arrayValue;
|
// Json::Value a = Json::arrayValue;
|
||||||
a[0U] = "ObjectEndMarker";
|
// a[0U] = "ObjectEndMarker";
|
||||||
Json::Value v = Json::objectValue;
|
// Json::Value v = Json::objectValue;
|
||||||
v[jss::nth] = 1;
|
// v[jss::nth] = 1;
|
||||||
v[jss::isVLEncoded] = false;
|
// v[jss::isVLEncoded] = false;
|
||||||
v[jss::isSerialized] = false;
|
// v[jss::isSerialized] = false;
|
||||||
v[jss::isSigningField] = true;
|
// v[jss::isSigningField] = true;
|
||||||
v[jss::type] = "STObject";
|
// v[jss::type] = "STObject";
|
||||||
a[1U] = v;
|
// a[1U] = v;
|
||||||
ret[jss::FIELDS][i++] = a;
|
// ret[jss::FIELDS][i++] = a;
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Json::Value a = Json::arrayValue;
|
// Json::Value a = Json::arrayValue;
|
||||||
a[0U] = "ArrayEndMarker";
|
// a[0U] = "ArrayEndMarker";
|
||||||
Json::Value v = Json::objectValue;
|
// Json::Value v = Json::objectValue;
|
||||||
v[jss::nth] = 1;
|
// v[jss::nth] = 1;
|
||||||
v[jss::isVLEncoded] = false;
|
// v[jss::isVLEncoded] = false;
|
||||||
v[jss::isSerialized] = false;
|
// v[jss::isSerialized] = false;
|
||||||
v[jss::isSigningField] = true;
|
// v[jss::isSigningField] = true;
|
||||||
v[jss::type] = "STArray";
|
// v[jss::type] = "STArray";
|
||||||
a[1U] = v;
|
// a[1U] = v;
|
||||||
ret[jss::FIELDS][i++] = a;
|
// ret[jss::FIELDS][i++] = a;
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Json::Value a = Json::arrayValue;
|
// Json::Value a = Json::arrayValue;
|
||||||
a[0U] = "hash";
|
// a[0U] = "hash";
|
||||||
Json::Value v = Json::objectValue;
|
// Json::Value v = Json::objectValue;
|
||||||
v[jss::nth] = 257;
|
// v[jss::nth] = 257;
|
||||||
v[jss::isVLEncoded] = false;
|
// v[jss::isVLEncoded] = false;
|
||||||
v[jss::isSerialized] = false;
|
// v[jss::isSerialized] = false;
|
||||||
v[jss::isSigningField] = false;
|
// v[jss::isSigningField] = false;
|
||||||
v[jss::type] = "Hash256";
|
// v[jss::type] = "Hash256";
|
||||||
a[1U] = v;
|
// a[1U] = v;
|
||||||
ret[jss::FIELDS][i++] = a;
|
// ret[jss::FIELDS][i++] = a;
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Json::Value a = Json::arrayValue;
|
// Json::Value a = Json::arrayValue;
|
||||||
a[0U] = "index";
|
// a[0U] = "index";
|
||||||
Json::Value v = Json::objectValue;
|
// Json::Value v = Json::objectValue;
|
||||||
v[jss::nth] = 258;
|
// v[jss::nth] = 258;
|
||||||
v[jss::isVLEncoded] = false;
|
// v[jss::isVLEncoded] = false;
|
||||||
v[jss::isSerialized] = false;
|
// v[jss::isSerialized] = false;
|
||||||
v[jss::isSigningField] = false;
|
// v[jss::isSigningField] = false;
|
||||||
v[jss::type] = "Hash256";
|
// v[jss::type] = "Hash256";
|
||||||
a[1U] = v;
|
// a[1U] = v;
|
||||||
ret[jss::FIELDS][i++] = a;
|
// ret[jss::FIELDS][i++] = a;
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Json::Value a = Json::arrayValue;
|
// Json::Value a = Json::arrayValue;
|
||||||
a[0U] = "taker_gets_funded";
|
// a[0U] = "taker_gets_funded";
|
||||||
Json::Value v = Json::objectValue;
|
// Json::Value v = Json::objectValue;
|
||||||
v[jss::nth] = 258;
|
// v[jss::nth] = 258;
|
||||||
v[jss::isVLEncoded] = false;
|
// v[jss::isVLEncoded] = false;
|
||||||
v[jss::isSerialized] = false;
|
// v[jss::isSerialized] = false;
|
||||||
v[jss::isSigningField] = false;
|
// v[jss::isSigningField] = false;
|
||||||
v[jss::type] = "Amount";
|
// v[jss::type] = "Amount";
|
||||||
a[1U] = v;
|
// a[1U] = v;
|
||||||
ret[jss::FIELDS][i++] = a;
|
// ret[jss::FIELDS][i++] = a;
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Json::Value a = Json::arrayValue;
|
// Json::Value a = Json::arrayValue;
|
||||||
a[0U] = "taker_pays_funded";
|
// a[0U] = "taker_pays_funded";
|
||||||
Json::Value v = Json::objectValue;
|
// Json::Value v = Json::objectValue;
|
||||||
v[jss::nth] = 259;
|
// v[jss::nth] = 259;
|
||||||
v[jss::isVLEncoded] = false;
|
// v[jss::isVLEncoded] = false;
|
||||||
v[jss::isSerialized] = false;
|
// v[jss::isSerialized] = false;
|
||||||
v[jss::isSigningField] = false;
|
// v[jss::isSigningField] = false;
|
||||||
v[jss::type] = "Amount";
|
// v[jss::type] = "Amount";
|
||||||
a[1U] = v;
|
// a[1U] = v;
|
||||||
ret[jss::FIELDS][i++] = a;
|
// ret[jss::FIELDS][i++] = a;
|
||||||
}
|
// }
|
||||||
|
|
||||||
for (auto const& [code, f] : ripple::SField::knownCodeToField)
|
// for (auto const& [code, f] : ripple::SField::knownCodeToField)
|
||||||
{
|
// {
|
||||||
if (f->fieldName == "")
|
// if (f->fieldName == "")
|
||||||
continue;
|
// continue;
|
||||||
|
|
||||||
Json::Value innerObj = Json::objectValue;
|
// Json::Value innerObj = Json::objectValue;
|
||||||
|
|
||||||
uint32_t fc = code & 0xFFU;
|
// uint32_t fc = code & 0xFFU;
|
||||||
uint32_t tc = code >> 16U;
|
// uint32_t tc = code >> 16U;
|
||||||
|
|
||||||
innerObj[jss::nth] = fc;
|
// innerObj[jss::nth] = fc;
|
||||||
|
|
||||||
innerObj[jss::isVLEncoded] =
|
// innerObj[jss::isVLEncoded] =
|
||||||
(tc == 7U /* Blob */ || tc == 8U /* AccountID */ ||
|
// (tc == 7U /* Blob */ || tc == 8U /* AccountID */ ||
|
||||||
tc == 19U /* Vector256 */);
|
// tc == 19U /* Vector256 */);
|
||||||
|
|
||||||
innerObj[jss::isSerialized] =
|
// innerObj[jss::isSerialized] =
|
||||||
(tc <
|
// (tc <
|
||||||
10000); /* TRANSACTION, LEDGER_ENTRY, VALIDATION, METADATA */
|
// 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;
|
// Json::Value innerArray = Json::arrayValue;
|
||||||
innerArray[0U] = f->fieldName;
|
// innerArray[0U] = f->fieldName;
|
||||||
innerArray[1U] = innerObj;
|
// innerArray[1U] = innerObj;
|
||||||
|
|
||||||
ret[jss::FIELDS][i++] = innerArray;
|
// ret[jss::FIELDS][i++] = innerArray;
|
||||||
}
|
// }
|
||||||
|
|
||||||
ret[jss::TRANSACTION_RESULTS] = Json::objectValue;
|
// ret[jss::TRANSACTION_RESULTS] = Json::objectValue;
|
||||||
for (auto [value, name] : magic_enum::enum_entries<TELcodes>())
|
// for (auto [value, name] : magic_enum::enum_entries<TELcodes>())
|
||||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||||
for (auto [value, name] : magic_enum::enum_entries<TEMcodes>())
|
// for (auto [value, name] : magic_enum::enum_entries<TEMcodes>())
|
||||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||||
for (auto [value, name] : magic_enum::enum_entries<TEFcodes>())
|
// for (auto [value, name] : magic_enum::enum_entries<TEFcodes>())
|
||||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||||
for (auto [value, name] : magic_enum::enum_entries<TERcodes>())
|
// for (auto [value, name] : magic_enum::enum_entries<TERcodes>())
|
||||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||||
for (auto [value, name] : magic_enum::enum_entries<TEScodes>())
|
// for (auto [value, name] : magic_enum::enum_entries<TEScodes>())
|
||||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||||
for (auto [value, name] : magic_enum::enum_entries<TECcodes>())
|
// for (auto [value, name] : magic_enum::enum_entries<TECcodes>())
|
||||||
ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
// ret[jss::TRANSACTION_RESULTS][STR(name)] = std::stoi(STR(value));
|
||||||
|
|
||||||
|
|
||||||
auto const translate_tt = [](std::string inp) -> std::string {
|
// auto const translate_tt = [](std::string inp) -> std::string {
|
||||||
if (inp == "PaychanClaim")
|
// if (inp == "PaychanClaim")
|
||||||
return "PaymentChannelClaim";
|
// return "PaymentChannelClaim";
|
||||||
if (inp == "PaychanCreate")
|
// if (inp == "PaychanCreate")
|
||||||
return "PaymentChannelCreate";
|
// return "PaymentChannelCreate";
|
||||||
if (inp == "PaychanFund")
|
// if (inp == "PaychanFund")
|
||||||
return "PaymentChannelFund";
|
// return "PaymentChannelFund";
|
||||||
if (inp == "RegularKeySet")
|
// if (inp == "RegularKeySet")
|
||||||
return "SetRegularKey";
|
// return "SetRegularKey";
|
||||||
if (inp == "HookSet")
|
// if (inp == "HookSet")
|
||||||
return "SetHook";
|
// return "SetHook";
|
||||||
return inp;
|
// return inp;
|
||||||
};
|
// };
|
||||||
|
|
||||||
ret[jss::TRANSACTION_TYPES] = Json::objectValue;
|
// ret[jss::TRANSACTION_TYPES] = Json::objectValue;
|
||||||
ret[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
|
// ret[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
|
||||||
for (auto [value, name] : magic_enum::enum_entries<TxType>())
|
// for (auto [value, name] : magic_enum::enum_entries<TxType>())
|
||||||
ret[jss::TRANSACTION_TYPES][translate_tt(translate(STR(name).substr(2)))] =
|
// ret[jss::TRANSACTION_TYPES][translate_tt(translate(STR(name).substr(2)))] =
|
||||||
std::stoi(STR(value));
|
// std::stoi(STR(value));
|
||||||
|
|
||||||
// generate hash
|
// // generate hash
|
||||||
{
|
// {
|
||||||
const std::string out = Json::FastWriter().write(ret);
|
// const std::string out = Json::FastWriter().write(ret);
|
||||||
defsHash =
|
// defsHash =
|
||||||
ripple::sha512Half(ripple::Slice{out.data(), out.size()});
|
// ripple::sha512Half(ripple::Slice{out.data(), out.size()});
|
||||||
ret[jss::hash] = to_string(*defsHash);
|
// ret[jss::hash] = to_string(*defsHash);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return ret;
|
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::Fee] = to_string(env.current()->fees().base);
|
||||||
jvn[jss::Sequence] = env.seq(alice);
|
jvn[jss::Sequence] = env.seq(alice);
|
||||||
jvn[jss::LastLedgerSequence] = env.current()->info().seq + 2;
|
jvn[jss::LastLedgerSequence] = env.current()->info().seq + 2;
|
||||||
auto jt = env.jtnofill(jvn);
|
auto jt = env.jtnofill(jvn, alice);
|
||||||
Serializer s;
|
Serializer s;
|
||||||
jt.stx->add(s);
|
jt.stx->add(s);
|
||||||
BEAST_EXPECT(env.rpc("submit", strHex(s.slice()))[jss::result][jss::engine_result] == "telREQUIRES_NETWORK_ID");
|
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. */
|
/** Create a JTx from parameters. */
|
||||||
template <class JsonValue, class... FN>
|
template <class JsonValue, class... FN>
|
||||||
JTx
|
JTx
|
||||||
jtnofill(JsonValue&& jv, FN const&... fN)
|
jt(JsonValue&& jv, Account account, FN const&... fN)
|
||||||
{
|
{
|
||||||
JTx jt(std::forward<JsonValue>(jv));
|
JTx jt(std::forward<JsonValue>(jv));
|
||||||
invoke(jt, fN...);
|
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);
|
jt.stx = st(jt);
|
||||||
return jt;
|
return jt;
|
||||||
}
|
}
|
||||||
@@ -518,6 +530,22 @@ public:
|
|||||||
{
|
{
|
||||||
apply(std::forward<JsonValue>(jv), fN...);
|
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. */
|
/** Return the TER for the last JTx. */
|
||||||
@@ -662,7 +690,10 @@ protected:
|
|||||||
std::unordered_map<std::string, std::string> const& headers = {});
|
std::unordered_map<std::string, std::string> const& headers = {});
|
||||||
|
|
||||||
void
|
void
|
||||||
autofill_sig(JTx& jt);
|
autofill_sig(JTx& jt, Account const& account);
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
acct_autofill(JTx& jt, Account const& account);
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
autofill(JTx& jt);
|
autofill(JTx& jt);
|
||||||
|
|||||||
@@ -402,14 +402,14 @@ Env::txid() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Env::autofill_sig(JTx& jt)
|
Env::autofill_sig(JTx& jt, Account const& account)
|
||||||
{
|
{
|
||||||
auto& jv = jt.jv;
|
auto& jv = jt.jv;
|
||||||
if (jt.signer)
|
if (jt.signer)
|
||||||
return jt.signer(*this, jt);
|
return jt.signer(*this, jt);
|
||||||
if (!jt.fill_sig)
|
if (!jt.fill_sig)
|
||||||
return;
|
return;
|
||||||
auto const account = lookup(jv[jss::Account].asString());
|
|
||||||
if (!app().checkSigs())
|
if (!app().checkSigs())
|
||||||
{
|
{
|
||||||
jv[jss::SigningPubKey] = strHex(account.pk().slice());
|
jv[jss::SigningPubKey] = strHex(account.pk().slice());
|
||||||
@@ -424,6 +424,31 @@ Env::autofill_sig(JTx& jt)
|
|||||||
jtx::sign(jv, account);
|
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
|
void
|
||||||
Env::autofill(JTx& jt)
|
Env::autofill(JTx& jt)
|
||||||
{
|
{
|
||||||
@@ -432,15 +457,16 @@ Env::autofill(JTx& jt)
|
|||||||
jtx::fill_fee(jv, *current());
|
jtx::fill_fee(jv, *current());
|
||||||
if (jt.fill_seq)
|
if (jt.fill_seq)
|
||||||
jtx::fill_seq(jv, *current());
|
jtx::fill_seq(jv, *current());
|
||||||
|
|
||||||
uint32_t networkID = app().config().NETWORK_ID;
|
uint32_t networkID = app().config().NETWORK_ID;
|
||||||
if (!jv.isMember(jss::NetworkID) && networkID > 1024)
|
if (!jv.isMember(jss::NetworkID) && networkID > 1024)
|
||||||
jv[jss::NetworkID] = std::to_string(networkID);
|
jv[jss::NetworkID] = std::to_string(networkID);
|
||||||
|
|
||||||
// Must come last
|
// Must come last
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
autofill_sig(jt);
|
auto const account = lookup(jv[jss::Account].asString());
|
||||||
|
autofill_sig(jt, account);
|
||||||
}
|
}
|
||||||
catch (parse_error const&)
|
catch (parse_error const&)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user