diff --git a/src/test/app/HostFuncImpl_test.cpp b/src/test/app/HostFuncImpl_test.cpp index 980d8d2b43..bd8ef8a379 100644 --- a/src/test/app/HostFuncImpl_test.cpp +++ b/src/test/app/HostFuncImpl_test.cpp @@ -1941,7 +1941,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite int const normalExp = 15; Bytes const floatIntMin = {0x99, 0x20, 0xc4, 0x9b, 0xa5, 0xe3, 0x53, 0xf8}; // -2^63 - Bytes const floatIntZero = {0xd8, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 0 + Bytes const floatIntZero = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 0 Bytes const floatIntMax = {0xd9, 0x20, 0xc4, 0x9b, 0xa5, 0xe3, 0x53, 0xf8}; // 2^63-1 Bytes const floatUIntMax = {0xd9, 0x46, 0x8d, 0xb8, 0xba, 0xc7, 0x10, 0xcb}; // 2^64 Bytes const floatMaxExp = {0xEC, 0x43, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // 1e(80+15) @@ -1954,8 +1954,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite Bytes const float1More = {0xD4, 0x83, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x01}; // 1.000 000 000 000 001 Bytes const float2 = {0xD4, 0x87, 0x1A, 0xFD, 0x49, 0x8D, 0x00, 0x00}; // 2 Bytes const float10 = {0xD4, 0xC3, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // 10 - Bytes const floatMaxXRP = {0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 2^62-1 - Bytes const floatMaxMPT = {0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 2^62-1 + Bytes const floatInvalidZero = {0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // INVALID std::string const invalid = "invalid_data"; @@ -2187,6 +2186,14 @@ struct HostFuncImpl_test : public beast::unit_test::suite result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } + { + auto const result = + hfs.floatCompare(makeSlice(floatInvalidZero), Slice()); + BEAST_EXPECT(!result) && + BEAST_EXPECT( + result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + { auto const result = hfs.floatCompare(makeSlice(float1), makeSlice(invalid)); @@ -2723,48 +2730,17 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatCompare(makeSlice(x), makeSlice(float10)); - BEAST_EXPECT(result && *result == 0); - } - - { - auto const result = - hfs.floatAdd(makeSlice(x), makeSlice(float10), 0); - if (BEAST_EXPECT(result)) - { - auto const result2 = - hfs.floatCompare(makeSlice(*result), makeSlice(*y)); - BEAST_EXPECT(result2 && *result2 == 0); - } - } - - // underflow - x[7] = 1; - { - auto const result = - hfs.floatDivide(makeSlice(x), makeSlice(float1More), 0); BEAST_EXPECT( !result && - result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } - { - auto const result = hfs.floatMultiply( - makeSlice(floatMaxXRP), makeSlice(floatIntZero), 0); - if (BEAST_EXPECT(result)) - { - auto const result2 = hfs.floatCompare( - makeSlice(*result), makeSlice(floatIntZero)); - BEAST_EXPECT(result2 && *result2 == 0); - } - } - - // overflow { auto const result = - hfs.floatAdd(makeSlice(floatMaxXRP), makeSlice(float1), 0); + hfs.floatAdd(makeSlice(float10), makeSlice(x), 0); BEAST_EXPECT( !result && - result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } // MPT @@ -2775,26 +2751,17 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = hfs.floatCompare(makeSlice(x), makeSlice(float10)); - BEAST_EXPECT(result && *result == 0); - } - - { - auto const result = - hfs.floatAdd(makeSlice(x), makeSlice(float10), 0); - if (BEAST_EXPECT(result)) - { - auto const result2 = - hfs.floatCompare(makeSlice(*result), makeSlice(*y)); - BEAST_EXPECT(result2 && *result2 == 0); - } - } - - { - auto const result = - hfs.floatAdd(makeSlice(floatMaxMPT), makeSlice(float1), 0); BEAST_EXPECT( !result && - result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + auto const result = + hfs.floatAdd(makeSlice(float10), makeSlice(x), 0); + BEAST_EXPECT( + !result && + result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } } diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index b423153a8a..4fb30242fb 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -546,10 +546,18 @@ struct Wasm_test : public beast::unit_test::suite TestHostFunctions hf(env, 0); auto re = runEscrowWasm(wasm, funcName, {}, &hf, 100'000); - if (BEAST_EXPECT(re.has_value())) - { - BEAST_EXPECT(re->result && (re->cost == 91'412)); - } + BEAST_EXPECT(re && re->result && (re->cost == 91'412)); + env.close(); + } + + { + std::string const wasmHex = float0Hex; + std::string const wasmStr = boost::algorithm::unhex(wasmHex); + std::vector const wasm(wasmStr.begin(), wasmStr.end()); + + TestHostFunctions hf(env, 0); + auto re = runEscrowWasm(wasm, funcName, {}, &hf, 100'000); + BEAST_EXPECT(re && re->result && (re->cost == 6'533)); env.close(); } } @@ -732,7 +740,6 @@ struct Wasm_test : public beast::unit_test::suite testFloat(); testCodecovWasm(); - testDisabledFloat(); // perfTest(); diff --git a/src/test/app/wasm_fixtures/fixtures.cpp b/src/test/app/wasm_fixtures/fixtures.cpp index 4a8e0e7385..3424de7006 100644 --- a/src/test/app/wasm_fixtures/fixtures.cpp +++ b/src/test/app/wasm_fixtures/fixtures.cpp @@ -12411,6 +12411,49 @@ extern std::string const floatHex = "6f62616c732b136e6f6e7472617070696e672d6670746f696e742b0f7265666572656e6365" "2d74797065732b087369676e2d657874"; +extern std::string const float0Hex = + "0061736d0100000001290560057f7f7f7f7f017f60047e7f7f7f017f6007" + "7f7f7f7f7f7f7f017f60047f7f7f7f017f6000017f025f0408686f73745f" + "6c6962057472616365000008686f73745f6c69620e666c6f61745f66726f" + "6d5f696e74000108686f73745f6c69620e666c6f61745f73756274726163" + "74000208686f73745f6c69620d666c6f61745f636f6d7061726500030302" + "010405030100110619037f01418080c0000b7f00419281c0000b7f0041a0" + "81c0000b072e04066d656d6f727902000666696e69736800040a5f5f6461" + "74615f656e6403010b5f5f686561705f6261736503020abe0201bb020101" + "7f23808080800041206b2200248080808000418080c08000411541004100" + "41001080808080001a200042003703084200200041086a41084100108180" + "8080001a20004200370310420a200041106a410841001081808080001a20" + "0042003703180240200041106a4108200041106a4108200041186a410841" + "001082808080004108460d00419580c08000411541004100410010808080" + "80001a0b02400240200041086a4108200041186a41081083808080000d00" + "41aa80c0800041174100410041001080808080001a0c010b41c180c08000" + "41164100410041001080808080001a0b02400240200041086a410841d780" + "c0800041081083808080000d0041df80c08000411a410041004100108080" + "8080001a0c010b41f980c0800041194100410041001080808080001a0b20" + "0041206a24808080800041010b0b9c010100418080c0000b92010a242424" + "20746573745f666c6f61745f30202424242020666c6f61742031302d3130" + "3a206661696c65642020666c6f6174203020636f6d706172653a20676f6f" + "642020666c6f6174203020636f6d706172653a2062616480000000000000" + "002020464c4f41545f5a45524f20636f6d706172653a20676f6f64202046" + "4c4f41545f5a45524f20636f6d706172653a20626164009502046e616d65" + "001110666c6f61745f74657374732e7761736d01da0105002b5f5a4e3878" + "72706c5f73746434686f7374357472616365313768616338383262323664" + "656162656436364501355f5a4e387872706c5f73746434686f7374313466" + "6c6f61745f66726f6d5f696e743137683032343066386533613839643139" + "39654502355f5a4e387872706c5f73746434686f73743134666c6f61745f" + "737562747261637431376864363430633135323334353432393563450334" + "5f5a4e387872706c5f73746434686f73743133666c6f61745f636f6d7061" + "72653137683663386465656231323864393638386645040666696e697368" + "071201000f5f5f737461636b5f706f696e746572090a0100072e726f6461" + "7461004d0970726f64756365727302086c616e6775616765010452757374" + "000c70726f6365737365642d6279010572757374631d312e38392e302028" + "32393438333838336520323032352d30382d3034290094010f7461726765" + "745f6665617475726573082b0b62756c6b2d6d656d6f72792b0f62756c6b" + "2d6d656d6f72792d6f70742b1663616c6c2d696e6469726563742d6f7665" + "726c6f6e672b0a6d756c746976616c75652b0f6d757461626c652d676c6f" + "62616c732b136e6f6e7472617070696e672d6670746f696e742b0f726566" + "6572656e63652d74797065732b087369676e2d657874"; + extern std::string const disabledFloatHex = "0061736d010000000108026000006000017f03030200010503010002063e" "0a7f004180080b7f004180080b7f004180100b7f004180100b7f00418090" diff --git a/src/test/app/wasm_fixtures/fixtures.h b/src/test/app/wasm_fixtures/fixtures.h index 948646404a..f3fdf66aae 100644 --- a/src/test/app/wasm_fixtures/fixtures.h +++ b/src/test/app/wasm_fixtures/fixtures.h @@ -63,4 +63,6 @@ extern std::string const codecovWasm; extern std::string const floatHex; +extern std::string const float0Hex; + extern std::string const disabledFloatHex; diff --git a/src/xrpld/app/wasm/detail/HostFuncImpl.cpp b/src/xrpld/app/wasm/detail/HostFuncImpl.cpp index d619c6e58f..441caddd77 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImpl.cpp +++ b/src/xrpld/app/wasm/detail/HostFuncImpl.cpp @@ -825,84 +825,66 @@ WasmHostFunctionsImpl::floatLog(Slice const& x, int32_t mode) class Number2 : public Number { -public: - enum Issue { XRP, MPT, IOU }; - protected: + static Bytes const FLOAT_NULL; + bool good_; - Issue issue_; public: - Number2(Slice const& data) : Number(0), good_(false), issue_(IOU) + Number2(Slice const& data) : Number(), good_(false) { if (data.size() != 8) return; + if (std::ranges::equal(FLOAT_NULL, data)) + { + good_ = true; + return; + } + uint64_t const v = SerialIter(data).get64(); - int const neg = (v & STAmount::cPositive) ? 1 : -1; + if (!(v & STAmount::cIssuedCurrency)) + return; - Number x; + int64_t const neg = (v & STAmount::cPositive) ? 1 : -1; + int32_t const e = static_cast((v >> (64 - 10)) & 0xFFull); + if (e < 1 || e > 177) + return; - if (v & STAmount::cIssuedCurrency) - { - // IOU - int32_t const e = - static_cast((v >> (64 - 10)) & ((1ull << 8) - 1)); - int64_t const m = neg * (v & ((1ull << 54) - 1)); - x = !m || (e < 1) ? Number() - : Number(m, e + IOUAmount::minExponent - 1); - if (m && (x.exponent() > IOUAmount::maxExponent)) - return; // invalid number - issue_ = IOU; - } - else if (v & STAmount::cMPToken) - { - // MPT - int64_t const m = neg * (v & ((1ull << 61) - 1)); - x = !m ? Number() : Number(m); - issue_ = MPT; - } - else - { - // XRP - int64_t const m = neg * (v & ((1ull << 61) - 1)); - x = !m ? Number() : Number(m); - issue_ = XRP; - } + int64_t const m = neg * static_cast(v & ((1ull << 54) - 1)); + if (!m) + return; + Number x(m, e + IOUAmount::minExponent - 1); *static_cast(this) = x; good_ = true; } - Number2() : Number(), good_(true), issue_(IOU) + Number2() : Number(), good_(true) { } - Number2(int64_t x) : Number(x), good_(true), issue_(IOU) + Number2(int64_t x) : Number(x), good_(true) { } - Number2(uint64_t x) : Number(0), good_(false), issue_(IOU) + Number2(uint64_t x) : Number(0), good_(false) { - if (x <= - std::numeric_limits< - std::invoke_result_t>:: - max()) - *static_cast(this) = Number(x); + using mtype = std::invoke_result_t; + if (x <= std::numeric_limits::max()) + *this = Number(x); else - { - *static_cast(this) = Number(x / 10, 1) + Number(x % 10); - } + *this = Number(x / 10, 1) + Number(x % 10); good_ = true; } Number2(int64_t mantissa, int32_t exponent) - : Number(mantissa, exponent), good_(true), issue_(IOU) + : Number(mantissa, exponent), good_(true) { } - Number2(Number const& n) : Number(n), good_(true), issue_(IOU) + Number2(Number const& n) : Number(n), good_(true) { } @@ -911,66 +893,38 @@ public: return good_; } - Issue - getIssue() const - { - return issue_; - } - - void - setIssue(Issue i) - { - issue_ = i; - } - Expected toBytes() const { uint64_t v = mantissa() >= 0 ? STAmount::cPositive : 0; + v |= STAmount::cIssuedCurrency; - if (issue_ == IOU) + uint64_t const absM = mantissa() >= 0 ? mantissa() : -mantissa(); + if (!absM) { - v |= STAmount::cIssuedCurrency; - - if (!mantissa()) + using etype = + std::invoke_result_t; + if (exponent() != std::numeric_limits::lowest()) { - if (exponent() != std::numeric_limits::lowest()) - return Unexpected( - HostFunctionError:: - FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE - } - else if (exponent() > IOUAmount::maxExponent) - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); - else if (exponent() < IOUAmount::minExponent) - return Number2().toBytes(); - - uint64_t absM = mantissa() >= 0 ? mantissa() : -mantissa(); - if (absM > ((1ull << 54) - 1)) return Unexpected( HostFunctionError:: FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE - - v |= absM; - - int const e = - (!mantissa() ? 0 : exponent()) - IOUAmount::minExponent + 1; - v |= ((uint64_t)e) << 54; + } + return FLOAT_NULL; } - else if (issue_ == MPT) + else if (absM > ((1ull << 54) - 1)) { - v |= STAmount::cMPToken; - uint64_t x = toUInt(61); - if (x == std::numeric_limits::max()) - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); - v |= x; - } - else - { - uint64_t x = toUInt(61); - if (x == std::numeric_limits::max()) - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); - v |= x; + return Unexpected( + HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE } + else if (exponent() > IOUAmount::maxExponent) + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + else if (exponent() < IOUAmount::minExponent) + return FLOAT_NULL; + + int const e = exponent() - IOUAmount::minExponent + 1; //+97 + v |= absM; + v |= ((uint64_t)e) << 54; Serializer msg; msg.add64(v); @@ -987,44 +941,11 @@ public: return data; } - -protected: - uint64_t - toUInt(unsigned bits) const - { - if (bits >= sizeof(uint64_t) * 8) - return std::numeric_limits::max(); // LCOV_EXCL_LINE - - uint64_t maxV = (1ull << bits) - 1; - uint64_t absM = mantissa() >= 0 ? mantissa() : -mantissa(); - - if (!absM) - return 0; - else if (exponent() < 0) - { - for (int i = 0; i > exponent(); --i) - { - // underflow - if (absM < 10) - return std::numeric_limits::max(); - absM /= 10; - } - } - else - { - for (int i = 0; i < exponent(); ++i) - { - // overflow - if (absM > maxV / 10) - return std::numeric_limits::max(); - absM *= 10; - } - } - - return absM > maxV ? std::numeric_limits::max() : absM; - } }; +Bytes const Number2::FLOAT_NULL = + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + struct SetRound { Number::rounding_mode oldMode_; @@ -1162,7 +1083,7 @@ floatAddImpl(Slice const& x, Slice const& y, int32_t mode) if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); Number2 res = xx + yy; - res.setIssue(xx.getIssue()); + return res.toBytes(); } // LCOV_EXCL_START @@ -1188,7 +1109,7 @@ floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode) if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); Number2 res = xx - yy; - res.setIssue(xx.getIssue()); + return res.toBytes(); } // LCOV_EXCL_START @@ -1214,7 +1135,7 @@ floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode) if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); Number2 res = xx * yy; - res.setIssue(xx.getIssue()); + return res.toBytes(); } // LCOV_EXCL_START @@ -1240,7 +1161,7 @@ floatDivideImpl(Slice const& x, Slice const& y, int32_t mode) if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); Number2 res = xx / yy; - res.setIssue(xx.getIssue()); + return res.toBytes(); } catch (...) @@ -1266,7 +1187,7 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); Number2 res(root(xx, n)); - res.setIssue(xx.getIssue()); + return res.toBytes(); } // LCOV_EXCL_START @@ -1296,7 +1217,7 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode) return Unexpected(HostFunctionError::INVALID_PARAMS); Number2 res(power(xx, n, 1)); - res.setIssue(xx.getIssue()); + return res.toBytes(); } catch (...) @@ -1319,7 +1240,7 @@ floatLogImpl(Slice const& x, int32_t mode) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); Number2 res(lg(xx)); - res.setIssue(xx.getIssue()); + return res.toBytes(); } // LCOV_EXCL_START