diff --git a/src/test/app/HostFuncImpl_test.cpp b/src/test/app/HostFuncImpl_test.cpp index de1468ca85..769c7741e8 100644 --- a/src/test/app/HostFuncImpl_test.cpp +++ b/src/test/app/HostFuncImpl_test.cpp @@ -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 locatorVec = {sfCredentialIDs.fieldCode, 0}; + Slice locator( + reinterpret_cast(locatorVec.data()), + locatorVec.size() * sizeof(int32_t)); + + auto const result = hfs.getTxNestedField(locator); + if (BEAST_EXPECTS( + result.has_value(), + std::to_string(static_cast(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 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(); diff --git a/src/xrpld/app/wasm/detail/HostFuncImplGetter.cpp b/src/xrpld/app/wasm/detail/HostFuncImplGetter.cpp index d137ec1fef..10f0d4a03e 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImplGetter.cpp +++ b/src/xrpld/app/wasm/detail/HostFuncImplGetter.cpp @@ -5,12 +5,13 @@ namespace xrpl { +typedef std::variant FieldValue; + namespace detail { static Expected 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(&data + 1); return Bytes{b, e}; } + break; case STI_UINT32: { auto const* num(static_cast 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(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 +getAnyFieldData(FieldValue const& variantObj) +{ + if (STBase const* const* obj = std::get_if(&variantObj)) + { + return getAnyFieldData(*obj); + } + else if (uint256 const* const* u = std::get_if(&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 +static Expected 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(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 +getArrayLen(FieldValue const& variantField) +{ + if (STBase const* const* field = std::get_if(&variantField)) + { + if ((*field)->getSType() == STI_VECTOR256) + return static_cast(*field)->size(); + if ((*field)->getSType() == STI_ARRAY) + return static_cast(*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 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(field)->size(); - - return sz; + return detail::getArrayLen(field); } Expected 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(field)->size(); - - return sz; + return detail::getArrayLen(field); } Expected @@ -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(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(field)->size(); - - return sz; + auto const& field = r.value(); + return detail::getArrayLen(field); } Expected @@ -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(field)->size(); - - return sz; + auto const& field = r.value(); + return detail::getArrayLen(field); } Expected @@ -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(field)->size(); - - return sz; + auto const& field = r.value(); + return detail::getArrayLen(field); } } // namespace xrpl