mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-03 08:46:46 +00:00
Add Vector256 support to the locator (#6131)
* add Vector256 nesting/length support * [WIP] add tests * fix tests * simplify with helper function * oops typo * remove static variable * respond to comments * STBaseOrUInt256->FieldValue * oops * add more tests for coverage * respond to comments
This commit is contained in:
@@ -29,6 +29,12 @@ toBytes(std::uint32_t value)
|
||||
return Bytes{b, e};
|
||||
}
|
||||
|
||||
static Bytes
|
||||
toBytes(uint256 const& value)
|
||||
{
|
||||
return Bytes{value.begin(), value.end()};
|
||||
}
|
||||
|
||||
static Bytes
|
||||
toBytes(Asset const& asset)
|
||||
{
|
||||
@@ -303,6 +309,11 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
testcase("getTxField");
|
||||
using namespace test::jtx;
|
||||
|
||||
std::string const credIdHex =
|
||||
"0011223344556677889900112233445566778899001122334455667788990011";
|
||||
uint256 credId;
|
||||
BEAST_EXPECT(credId.parseHex(credIdHex));
|
||||
|
||||
Env env{*this};
|
||||
OpenView ov{*env.current()};
|
||||
STTx const stx = STTx(ttESCROW_FINISH, [&](auto& obj) {
|
||||
@@ -310,6 +321,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
obj.setAccountID(sfOwner, env.master.id());
|
||||
obj.setFieldU32(sfOfferSequence, env.seq(env.master));
|
||||
obj.setFieldArray(sfMemos, STArray{});
|
||||
STVector256 credIds;
|
||||
credIds.push_back(credId);
|
||||
obj.setFieldV256(sfCredentialIDs, credIds);
|
||||
});
|
||||
ApplyContext ac = createApplyContext(env, ov, stx);
|
||||
auto const dummyEscrow =
|
||||
@@ -340,6 +354,12 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(
|
||||
memos.error() == HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
auto const credentialIds = hfs.getTxField(sfCredentialIDs);
|
||||
if (BEAST_EXPECT(!credentialIds.has_value()))
|
||||
BEAST_EXPECTS(
|
||||
credentialIds.error() == HostFunctionError::NOT_LEAF_FIELD,
|
||||
std::to_string(HfErrorToInt(credentialIds.error())));
|
||||
|
||||
auto const nonField = hfs.getTxField(sfInvalid);
|
||||
if (BEAST_EXPECT(!nonField.has_value()))
|
||||
BEAST_EXPECT(
|
||||
@@ -450,6 +470,15 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(*amountField == toBytes(XRP(100)));
|
||||
}
|
||||
|
||||
// Should return the PreviousTxnID field from the escrow ledger object
|
||||
auto const previousTxnId =
|
||||
hfs.getCurrentLedgerObjField(sfPreviousTxnID);
|
||||
if (BEAST_EXPECT(previousTxnId.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
*previousTxnId == toBytes(env.tx()->getTransactionID()));
|
||||
}
|
||||
|
||||
// Should return nullopt for a field not present
|
||||
auto const notPresent = hfs.getCurrentLedgerObjField(sfOwner);
|
||||
BEAST_EXPECT(
|
||||
@@ -541,6 +570,11 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
Env env{*this};
|
||||
OpenView ov{*env.current()};
|
||||
|
||||
std::string const credIdHex =
|
||||
"0011223344556677889900112233445566778899001122334455667788990011";
|
||||
uint256 credId;
|
||||
BEAST_EXPECT(credId.parseHex(credIdHex));
|
||||
|
||||
// Create a transaction with a nested array field
|
||||
STTx const stx = STTx(ttESCROW_FINISH, [&](auto& obj) {
|
||||
obj.setAccountID(sfAccount, env.master.id());
|
||||
@@ -549,6 +583,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
memoObj.setFieldVL(sfMemoData, Slice("hello", 5));
|
||||
memos.push_back(memoObj);
|
||||
obj.setFieldArray(sfMemos, memos);
|
||||
STVector256 credIds;
|
||||
credIds.push_back(credId);
|
||||
obj.setFieldV256(sfCredentialIDs, credIds);
|
||||
});
|
||||
|
||||
ApplyContext ac = createApplyContext(env, ov, stx);
|
||||
@@ -578,6 +615,24 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Locator for sfCredentialIDs[0]
|
||||
std::vector<int32_t> locatorVec = {sfCredentialIDs.fieldCode, 0};
|
||||
Slice locator(
|
||||
reinterpret_cast<uint8_t const*>(locatorVec.data()),
|
||||
locatorVec.size() * sizeof(int32_t));
|
||||
|
||||
auto const result = hfs.getTxNestedField(locator);
|
||||
if (BEAST_EXPECTS(
|
||||
result.has_value(),
|
||||
std::to_string(static_cast<int>(result.error()))))
|
||||
{
|
||||
std::string credIdResult(
|
||||
result.value().begin(), result.value().end());
|
||||
BEAST_EXPECT(strHex(credIdResult) == credIdHex);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// can use the nested locator for base fields too
|
||||
std::vector<int32_t> locatorVec = {sfAccount.fieldCode};
|
||||
@@ -637,6 +692,11 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
sfMemoData.fieldCode},
|
||||
HostFunctionError::INDEX_OUT_OF_BOUNDS);
|
||||
|
||||
// Locator for non-existent index
|
||||
expectError(
|
||||
{sfCredentialIDs.fieldCode, 1}, // index 1 does not exist
|
||||
HostFunctionError::INDEX_OUT_OF_BOUNDS);
|
||||
|
||||
// Locator for non-existent nested field
|
||||
expectError(
|
||||
{sfMemos.fieldCode,
|
||||
@@ -661,6 +721,10 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
// Locator for STArray
|
||||
expectError({sfMemos.fieldCode}, HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
// Locator for STVector256
|
||||
expectError(
|
||||
{sfCredentialIDs.fieldCode}, HostFunctionError::NOT_LEAF_FIELD);
|
||||
|
||||
// Locator for nesting into non-array/object field
|
||||
expectError(
|
||||
{sfAccount.fieldCode, // sfAccount is not an array or object
|
||||
@@ -956,6 +1020,11 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
testcase("getTxArrayLen");
|
||||
using namespace test::jtx;
|
||||
|
||||
std::string const credIdHex =
|
||||
"0011223344556677889900112233445566778899001122334455667788990011";
|
||||
uint256 credId;
|
||||
BEAST_EXPECT(credId.parseHex(credIdHex));
|
||||
|
||||
Env env{*this};
|
||||
OpenView ov{*env.current()};
|
||||
|
||||
@@ -974,6 +1043,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
memos.push_back(memoObj);
|
||||
}
|
||||
obj.setFieldArray(sfMemos, memos);
|
||||
STVector256 credIds;
|
||||
credIds.push_back(credId);
|
||||
obj.setFieldV256(sfCredentialIDs, credIds);
|
||||
});
|
||||
|
||||
ApplyContext ac = createApplyContext(env, ov, stx);
|
||||
@@ -996,6 +1068,11 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
if (BEAST_EXPECT(!missingArray.has_value()))
|
||||
BEAST_EXPECT(
|
||||
missingArray.error() == HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
// Should return 1 for sfCredentialIDs
|
||||
auto const credIdsLen = hfs.getTxArrayLen(sfCredentialIDs);
|
||||
if (BEAST_EXPECT(credIdsLen.has_value()))
|
||||
BEAST_EXPECT(credIdsLen.value() == 1);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2195,9 +2272,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
// clang-format on
|
||||
|
||||
void
|
||||
testFloatTrace()
|
||||
testTraceFloat()
|
||||
{
|
||||
testcase("FloatTrace");
|
||||
testcase("traceFloat");
|
||||
using namespace test::jtx;
|
||||
|
||||
{
|
||||
@@ -2251,7 +2328,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
void
|
||||
testFloatFromInt()
|
||||
{
|
||||
testcase("FloatFromInt");
|
||||
testcase("floatFromInt");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
@@ -2298,7 +2375,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
void
|
||||
testFloatFromUint()
|
||||
{
|
||||
testcase("FloatFromUint");
|
||||
testcase("floatFromUint");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
@@ -2339,7 +2416,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
void
|
||||
testFloatSet()
|
||||
{
|
||||
testcase("FloatSet");
|
||||
testcase("floatSet");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
@@ -2426,7 +2503,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
void
|
||||
testFloatCompare()
|
||||
{
|
||||
testcase("FloatCompare");
|
||||
testcase("floatCompare");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
@@ -3071,7 +3148,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite
|
||||
void
|
||||
testFloats()
|
||||
{
|
||||
testFloatTrace();
|
||||
testTraceFloat();
|
||||
testFloatFromInt();
|
||||
testFloatFromUint();
|
||||
testFloatSet();
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
typedef std::variant<STBase const*, uint256 const*> FieldValue;
|
||||
|
||||
namespace detail {
|
||||
|
||||
static Expected<Bytes, HostFunctionError>
|
||||
getAnyFieldData(STBase const* obj)
|
||||
{
|
||||
// auto const& fname = obj.getFName();
|
||||
if (!obj)
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
@@ -25,6 +26,7 @@ getAnyFieldData(STBase const* obj)
|
||||
// LCOV_EXCL_STOP
|
||||
case STI_OBJECT:
|
||||
case STI_ARRAY:
|
||||
case STI_VECTOR256:
|
||||
return Unexpected(HostFunctionError::NOT_LEAF_FIELD);
|
||||
break;
|
||||
case STI_ACCOUNT: {
|
||||
@@ -62,6 +64,7 @@ getAnyFieldData(STBase const* obj)
|
||||
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
|
||||
return Bytes{b, e};
|
||||
}
|
||||
break;
|
||||
case STI_UINT32: {
|
||||
auto const* num(static_cast<STInteger<std::uint32_t> const*>(obj));
|
||||
std::uint32_t const data = num->value();
|
||||
@@ -70,6 +73,12 @@ getAnyFieldData(STBase const* obj)
|
||||
return Bytes{b, e};
|
||||
}
|
||||
break;
|
||||
case STI_UINT256: {
|
||||
auto const* uint256Obj(static_cast<STUInt256 const*>(obj));
|
||||
auto const& data = uint256Obj->value();
|
||||
return Bytes{data.begin(), data.end()};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break; // default to serializer
|
||||
}
|
||||
@@ -81,6 +90,21 @@ getAnyFieldData(STBase const* obj)
|
||||
return data;
|
||||
}
|
||||
|
||||
static Expected<Bytes, HostFunctionError>
|
||||
getAnyFieldData(FieldValue const& variantObj)
|
||||
{
|
||||
if (STBase const* const* obj = std::get_if<STBase const*>(&variantObj))
|
||||
{
|
||||
return getAnyFieldData(*obj);
|
||||
}
|
||||
else if (uint256 const* const* u = std::get_if<uint256 const*>(&variantObj))
|
||||
{
|
||||
return Bytes((*u)->begin(), (*u)->end());
|
||||
}
|
||||
|
||||
return Unexpected(HostFunctionError::INTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
static inline bool
|
||||
noField(STBase const* field)
|
||||
{
|
||||
@@ -88,7 +112,7 @@ noField(STBase const* field)
|
||||
(STI_UNKNOWN == field->getSType());
|
||||
}
|
||||
|
||||
static Expected<STBase const*, HostFunctionError>
|
||||
static Expected<FieldValue, HostFunctionError>
|
||||
locateField(STObject const& obj, Slice const& locator)
|
||||
{
|
||||
if (locator.empty() || (locator.size() & 3)) // must be multiple of 4
|
||||
@@ -141,6 +165,13 @@ locateField(STObject const& obj, Slice const& locator)
|
||||
auto const& fname(*it->second);
|
||||
field = o->peekAtPField(fname);
|
||||
}
|
||||
else if (STI_VECTOR256 == field->getSType())
|
||||
{
|
||||
auto const* v = static_cast<STVector256 const*>(field);
|
||||
if (sfieldCode >= v->size())
|
||||
return Unexpected(HostFunctionError::INDEX_OUT_OF_BOUNDS);
|
||||
return FieldValue(&(v->operator[](sfieldCode)));
|
||||
}
|
||||
else // simple field must be the last one
|
||||
{
|
||||
return Unexpected(HostFunctionError::LOCATOR_MALFORMED);
|
||||
@@ -150,7 +181,22 @@ locateField(STObject const& obj, Slice const& locator)
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
}
|
||||
|
||||
return field;
|
||||
return FieldValue(field);
|
||||
}
|
||||
|
||||
static inline Expected<int32_t, HostFunctionError>
|
||||
getArrayLen(FieldValue const& variantField)
|
||||
{
|
||||
if (STBase const* const* field = std::get_if<STBase const*>(&variantField))
|
||||
{
|
||||
if ((*field)->getSType() == STI_VECTOR256)
|
||||
return static_cast<STVector256 const*>(*field)->size();
|
||||
if ((*field)->getSType() == STI_ARRAY)
|
||||
return static_cast<STArray const*>(*field)->size();
|
||||
}
|
||||
// uint256 is not an array so that variant should still return NO_ARRAY
|
||||
|
||||
return Unexpected(HostFunctionError::NO_ARRAY); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
@@ -256,24 +302,20 @@ WasmHostFunctionsImpl::getLedgerObjNestedField(
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getTxArrayLen(SField const& fname)
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
auto const* field = ctx.tx.peekAtPField(fname);
|
||||
if (detail::noField(field))
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY); // LCOV_EXCL_LINE
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
return detail::getArrayLen(field);
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname)
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
auto const sle = getCurrentLedgerObj();
|
||||
@@ -284,11 +326,7 @@ WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname)
|
||||
if (detail::noField(field))
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY); // LCOV_EXCL_LINE
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
return detail::getArrayLen(field);
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
@@ -296,7 +334,7 @@ WasmHostFunctionsImpl::getLedgerObjArrayLen(
|
||||
int32_t cacheIdx,
|
||||
SField const& fname)
|
||||
{
|
||||
if (fname.fieldType != STI_ARRAY)
|
||||
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
|
||||
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
|
||||
@@ -307,12 +345,7 @@ WasmHostFunctionsImpl::getLedgerObjArrayLen(
|
||||
if (detail::noField(field))
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY); // LCOV_EXCL_LINE
|
||||
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
return detail::getArrayLen(field);
|
||||
}
|
||||
|
||||
// Subsection: nested array length getters
|
||||
@@ -324,12 +357,8 @@ WasmHostFunctionsImpl::getTxNestedArrayLen(Slice const& locator)
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
auto const& field = r.value();
|
||||
return detail::getArrayLen(field);
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
@@ -342,12 +371,8 @@ WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(Slice const& locator)
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
auto const& field = r.value();
|
||||
return detail::getArrayLen(field);
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
@@ -363,12 +388,8 @@ WasmHostFunctionsImpl::getLedgerObjNestedArrayLen(
|
||||
if (!r)
|
||||
return Unexpected(r.error());
|
||||
|
||||
auto const* field = r.value();
|
||||
if (field->getSType() != STI_ARRAY)
|
||||
return Unexpected(HostFunctionError::NO_ARRAY);
|
||||
int32_t const sz = static_cast<STArray const*>(field)->size();
|
||||
|
||||
return sz;
|
||||
auto const& field = r.value();
|
||||
return detail::getArrayLen(field);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
Reference in New Issue
Block a user