-
Notifications
You must be signed in to change notification settings - Fork 1.6k
feat: add WASM host functions (Wasmi version) #6075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: ripple/wasmi
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## ripple/wasmi #6075 +/- ##
==============================================
+ Coverage 79.1% 79.6% +0.5%
==============================================
Files 836 845 +9
Lines 71261 73319 +2058
Branches 8303 8363 +60
==============================================
+ Hits 56393 58367 +1974
- Misses 14868 14952 +84
🚀 New features to boost your workflow:
|
wasm memory, table, and trap unit tests
| class Number2 : public Number | ||
| { | ||
| protected: | ||
| static Bytes const FLOAT_NULL; | ||
|
|
||
| bool good_; | ||
|
|
||
| public: | ||
| 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(); | ||
| if (!(v & STAmount::cIssuedCurrency)) | ||
| return; | ||
|
|
||
| int64_t const neg = (v & STAmount::cPositive) ? 1 : -1; | ||
| int32_t const e = static_cast<uint8_t>((v >> (64 - 10)) & 0xFFull); | ||
| if (e < 1 || e > 177) | ||
| return; | ||
|
|
||
| int64_t const m = neg * static_cast<int64_t>(v & ((1ull << 54) - 1)); | ||
| if (!m) | ||
| return; | ||
|
|
||
| Number x(m, e + IOUAmount::minExponent - 1); | ||
| *static_cast<Number*>(this) = x; | ||
| good_ = true; | ||
| } | ||
|
|
||
| Number2() : Number(), good_(true) | ||
| { | ||
| } | ||
|
|
||
| Number2(int64_t x) : Number(x), good_(true) | ||
| { | ||
| } | ||
|
|
||
| Number2(uint64_t x) : Number(0), good_(false) | ||
| { | ||
| using mtype = std::invoke_result_t<decltype(&Number::mantissa), Number>; | ||
| if (x <= std::numeric_limits<mtype>::max()) | ||
| *this = Number(x); | ||
| else | ||
| *this = Number(x / 10, 1) + Number(x % 10); | ||
|
|
||
| good_ = true; | ||
| } | ||
|
|
||
| Number2(int64_t mantissa, int32_t exponent) | ||
| : Number(mantissa, exponent), good_(true) | ||
| { | ||
| } | ||
|
|
||
| Number2(Number const& n) : Number(n), good_(true) | ||
| { | ||
| } | ||
|
|
||
| operator bool() const | ||
| { | ||
| return good_; | ||
| } | ||
|
|
||
| Expected<Bytes, HostFunctionError> | ||
| toBytes() const | ||
| { | ||
| uint64_t v = mantissa() >= 0 ? STAmount::cPositive : 0; | ||
| v |= STAmount::cIssuedCurrency; | ||
|
|
||
| uint64_t const absM = mantissa() >= 0 ? mantissa() : -mantissa(); | ||
| if (!absM) | ||
| { | ||
| using etype = | ||
| std::invoke_result_t<decltype(&Number::exponent), Number>; | ||
| if (exponent() != std::numeric_limits<etype>::lowest()) | ||
| { | ||
| return Unexpected( | ||
| HostFunctionError:: | ||
| FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE | ||
| } | ||
| return FLOAT_NULL; | ||
| } | ||
| else if (absM > ((1ull << 54) - 1)) | ||
| { | ||
| 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); | ||
| auto const data = msg.getData(); | ||
|
|
||
| #ifdef DEBUG_OUTPUT | ||
| std::cout << "m: " << std::setw(20) << mantissa() | ||
| << ", e: " << std::setw(12) << exponent() << ", hex: "; | ||
| std::cout << std::hex << std::uppercase << std::setfill('0'); | ||
| for (auto const& c : data) | ||
| std::cout << std::setw(2) << (unsigned)c << " "; | ||
| std::cout << std::dec << std::setfill(' ') << std::endl; | ||
| #endif | ||
|
|
||
| return data; | ||
| } | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm missing the point of having this class.
Most of it's functionality could be a part of base Number. The only part which isn't, toBytes function, could be a free function in this file, accepting number as an argument.
This also could mean that most of this could be a separate independent PR to develop, but I'm not forcing this in case you don't dant to do that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conversion from Slice shouldn't be part of the Number, uint64_t shouldn't be the part of the Number (as it can lead to silent conversion, and I don't want to check every usage of the numbers). toBytes must be private (static to this file) and its appearance in the class just because class is present here, and not to conflict with other possible toBytes.
It is good helper class only for wasm "float" functions and will not interfere with the other modules
| // Continued fraction approximation of ln(x) | ||
| static Number | ||
| ln(Number const& x, unsigned iterations = 50) | ||
| { | ||
| if (x <= 0) | ||
| throw std::runtime_error("Not positive value"); | ||
|
|
||
| Number const z = (x - 1) / (x + 1); | ||
| Number const zz = z * z; | ||
| Number denom = Number(1, -10); | ||
|
|
||
| // Construct the fraction from the bottom up | ||
| for (int i = iterations; i > 0; --i) | ||
| { | ||
| Number k(2 * i - 1); | ||
| denom = k - (i * i * zz / denom); | ||
| } | ||
|
|
||
| auto const r = 2 * z / denom; | ||
| return r; | ||
| } | ||
|
|
||
| Number | ||
| lg(Number const& x) | ||
| { | ||
| static Number const ln10 = ln(Number(10)); | ||
|
|
||
| if (x <= Number(10)) | ||
| { | ||
| auto const r = ln(x) / ln10; | ||
| return r; | ||
| } | ||
|
|
||
| // ln(x) = ln(normX * 10^norm) = ln(normX) + norm * ln(10) | ||
| int diffExp = 15 + x.exponent(); | ||
| Number const normalX = x / Number(1, diffExp); // (1 <= normalX < 10) | ||
| auto const lnX = ln(normalX) + diffExp * ln10; | ||
|
|
||
| auto const r = lnX / ln10; | ||
| return r; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see test covering these functions are in TestHostFunctions.h. Since you've added this to libxrpl, those tests should be here as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also could be a separate small PR to develop ideally, but not forcing this
High Level Overview of Change
This PR adds all the Wasmi integration code and the host functions specified in XLS-102. It also adds tests for all of this.
Note for reviewers: 10k lines of this PR are just WASM-compiled test fixtures, and another 4k lines are WASM test fixture source code. So while this PR is still quite large, it's not as large as it seems on the surface.
Context of Change
This PR depends on #5999 and replaces #5791
It is part of an effort to split up the Smart Escrow work into smaller, more managable-to-review PRs.
XLS-102: XRPLF/XRPL-Standards#303
Type of Change
.gitignore, formatting, dropping support for older tooling)API Impact
N/A
Test Plan
Several tests are added to this PR.