mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-04 05:55:25 +00:00
Compare commits
147 Commits
copilot/co
...
ripple/was
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba03a8a9d2 | ||
|
|
7c8279ec83 | ||
|
|
0418ffb26a | ||
|
|
b2627039f6 | ||
|
|
6c1a92fe93 | ||
|
|
7813683091 | ||
|
|
b814a09a08 | ||
|
|
6d369e0f02 | ||
|
|
b182430178 | ||
|
|
fe31cdc9f6 | ||
|
|
ff4520cc45 | ||
|
|
8f97ec3bde | ||
|
|
e85e7b1b1a | ||
|
|
803a344c65 | ||
|
|
4eb34f381a | ||
|
|
72fffb6e51 | ||
|
|
f7ee580f01 | ||
|
|
122d405750 | ||
|
|
c1c1b4ea67 | ||
|
|
977caea0a5 | ||
|
|
d7ed6d6512 | ||
|
|
f1f2e2629f | ||
|
|
917c610f96 | ||
|
|
317e533d81 | ||
|
|
4160677878 | ||
|
|
df98db1452 | ||
|
|
673476ef1b | ||
|
|
8bc6f9cd70 | ||
|
|
ba5debfecd | ||
|
|
f4a27c9b6d | ||
|
|
fd1cb318e3 | ||
|
|
8c3544a58c | ||
|
|
ed5139d4e3 | ||
|
|
42494dd4cf | ||
|
|
ce84cc8b44 | ||
|
|
9a9a7aab01 | ||
|
|
209a1a6ffa | ||
|
|
fc35a9f9c8 | ||
|
|
c5e50aa221 | ||
|
|
074b1f00d5 | ||
|
|
7a9d245950 | ||
|
|
1809fe07f2 | ||
|
|
409c67494a | ||
|
|
c626b6403a | ||
|
|
81cbc91927 | ||
|
|
1c812a6c4d | ||
|
|
0724927799 | ||
|
|
d83ec96848 | ||
|
|
375dd50b35 | ||
|
|
419d53ec4c | ||
|
|
d4d70d5675 | ||
|
|
6ab15f8377 | ||
|
|
91f3d51f3d | ||
|
|
9ed60b45f8 | ||
|
|
d5c53dcfd2 | ||
|
|
e94321fb41 | ||
|
|
bbc28b3b1c | ||
|
|
843e981c8a | ||
|
|
5aab274b7a | ||
|
|
2c30e41191 | ||
|
|
8ea5106b0b | ||
|
|
f57f67a8ae | ||
|
|
a98269f049 | ||
|
|
b66bc47ca9 | ||
|
|
0e9c7458bb | ||
|
|
1d89940653 | ||
|
|
1a1a6806ec | ||
|
|
1977df9c2e | ||
|
|
6c95548df5 | ||
|
|
69ab39d658 | ||
|
|
b9eb66eecc | ||
|
|
881087dd3d | ||
|
|
90e0bbd0fc | ||
|
|
b57df290de | ||
|
|
8a403f1241 | ||
|
|
6d2640871d | ||
|
|
c145598ff9 | ||
|
|
50e5608d86 | ||
|
|
7a7b96107c | ||
|
|
500bb68831 | ||
|
|
53eb0f60bc | ||
|
|
41205ae928 | ||
|
|
c33b0ae463 | ||
|
|
16087c9680 | ||
|
|
56bc6d58f6 | ||
|
|
ef5d335e09 | ||
|
|
25c3060fef | ||
|
|
ce9f0b38a4 | ||
|
|
35f7cbf772 | ||
|
|
0db564d261 | ||
|
|
427b7ea104 | ||
|
|
7bf6878b4b | ||
|
|
0bc1a115ff | ||
|
|
334bcfa5ef | ||
|
|
106dea4559 | ||
|
|
3ffdcf8114 | ||
|
|
4021a7eb28 | ||
|
|
0690fda0f1 | ||
|
|
d0cc48c6d3 | ||
|
|
d66e3c949e | ||
|
|
0c65a386b5 | ||
|
|
29f5430881 | ||
|
|
101f285bcd | ||
|
|
286dc6322b | ||
|
|
c9346cd40d | ||
|
|
1c5683ec78 | ||
|
|
9bee155d59 | ||
|
|
f34b05f4de | ||
|
|
97ce25f4ce | ||
|
|
9e14c14a26 | ||
|
|
c507880d8f | ||
|
|
3f8328bbf8 | ||
|
|
c10a5f9ef6 | ||
|
|
3c141de695 | ||
|
|
da2b9455f2 | ||
|
|
cb622488c0 | ||
|
|
32f971fec6 | ||
|
|
8dea76baa4 | ||
|
|
299fbe04c4 | ||
|
|
57fc1df7d7 | ||
|
|
eaba76f9e6 | ||
|
|
cb702cc238 | ||
|
|
b69b4a0a4a | ||
|
|
50d6072a73 | ||
|
|
d24cd50e61 | ||
|
|
9f5875158c | ||
|
|
c3dc33c861 | ||
|
|
6be8f2124c | ||
|
|
edfed06001 | ||
|
|
1c646dba91 | ||
|
|
6781068058 | ||
|
|
cfe57c1dfe | ||
|
|
c34d09a971 | ||
|
|
ebd90c4742 | ||
|
|
ba52d34828 | ||
|
|
1b6312afb3 | ||
|
|
bf32dc2e72 | ||
|
|
a15d65f7a2 | ||
|
|
2de8488855 | ||
|
|
129aa4bfaa | ||
|
|
b1d70db63b | ||
|
|
f03c3aafe4 | ||
|
|
51a9f106d1 | ||
|
|
bfc048e3fe | ||
|
|
83418644f7 | ||
|
|
dbc9dd5bfc | ||
|
|
45ab15d4b5 |
@@ -272,6 +272,7 @@ words:
|
||||
- venv
|
||||
- vfalco
|
||||
- vinnie
|
||||
- wasmi
|
||||
- wextra
|
||||
- wptr
|
||||
- writeme
|
||||
|
||||
6
.github/CODEOWNERS
vendored
6
.github/CODEOWNERS
vendored
@@ -1,8 +1,2 @@
|
||||
# Allow anyone to review any change by default.
|
||||
*
|
||||
|
||||
# Require the rpc-reviewers team to review changes to the rpc code.
|
||||
include/xrpl/protocol/ @xrplf/rpc-reviewers
|
||||
src/libxrpl/protocol/ @xrplf/rpc-reviewers
|
||||
src/xrpld/rpc/ @xrplf/rpc-reviewers
|
||||
src/xrpld/app/misc/ @xrplf/rpc-reviewers
|
||||
|
||||
@@ -153,6 +153,7 @@ tests.libxrpl > xrpl.json
|
||||
tests.libxrpl > xrpl.net
|
||||
xrpl.core > xrpl.basics
|
||||
xrpl.core > xrpl.json
|
||||
xrpl.core > xrpl.ledger
|
||||
xrpl.json > xrpl.basics
|
||||
xrpl.ledger > xrpl.basics
|
||||
xrpl.ledger > xrpl.protocol
|
||||
|
||||
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
|
||||
run-hooks:
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@282890f46d6921249d5659dd38babcb0bd8aef48
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@320be44621ca2a080f05aeb15817c44b84518108
|
||||
with:
|
||||
runs_on: ubuntu-latest
|
||||
container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-ab4d1f0" }'
|
||||
|
||||
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Get number of processors
|
||||
uses: XRPLF/actions/get-nproc@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
|
||||
id: nproc
|
||||
with:
|
||||
subtract: ${{ env.NPROC_SUBTRACT }}
|
||||
|
||||
@@ -101,13 +101,13 @@ jobs:
|
||||
steps:
|
||||
- name: Cleanup workspace (macOS and Windows)
|
||||
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
||||
uses: XRPLF/actions/cleanup-workspace@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/cleanup-workspace@cf0433aa74563aead044a1e395610c96d65a37cf
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@f05cab7b8541eee6473aa42beb9d2fe35608a190
|
||||
uses: XRPLF/actions/prepare-runner@2cbf481018d930656e9276fcc20dc0e3a0be5b6d
|
||||
with:
|
||||
enable_ccache: ${{ inputs.ccache_enabled }}
|
||||
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
uses: ./.github/actions/print-env
|
||||
|
||||
- name: Get number of processors
|
||||
uses: XRPLF/actions/get-nproc@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
|
||||
id: nproc
|
||||
with:
|
||||
subtract: ${{ inputs.nproc_subtract }}
|
||||
|
||||
6
.github/workflows/upload-conan-deps.yml
vendored
6
.github/workflows/upload-conan-deps.yml
vendored
@@ -64,13 +64,13 @@ jobs:
|
||||
steps:
|
||||
- name: Cleanup workspace (macOS and Windows)
|
||||
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
||||
uses: XRPLF/actions/cleanup-workspace@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/cleanup-workspace@cf0433aa74563aead044a1e395610c96d65a37cf
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@f05cab7b8541eee6473aa42beb9d2fe35608a190
|
||||
uses: XRPLF/actions/prepare-runner@2cbf481018d930656e9276fcc20dc0e3a0be5b6d
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
uses: ./.github/actions/print-env
|
||||
|
||||
- name: Get number of processors
|
||||
uses: XRPLF/actions/get-nproc@2ece4ec6ab7de266859a6f053571425b2bd684b6
|
||||
uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf
|
||||
id: nproc
|
||||
with:
|
||||
subtract: ${{ env.NPROC_SUBTRACT }}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -64,6 +64,9 @@ DerivedData
|
||||
/.vs/
|
||||
/.vscode/
|
||||
|
||||
# zed IDE.
|
||||
/.zed/
|
||||
|
||||
# AI tools.
|
||||
/.augment
|
||||
/.claude
|
||||
|
||||
200
API-CHANGELOG.md
200
API-CHANGELOG.md
@@ -6,90 +6,85 @@ For info about how [API versioning](https://xrpl.org/request-formatting.html#api
|
||||
|
||||
The API version controls the API behavior you see. This includes what properties you see in responses, what parameters you're permitted to send in requests, and so on. You specify the API version in each of your requests. When a breaking change is introduced to the `rippled` API, a new version is released. To avoid breaking your code, you should set (or increase) your version when you're ready to upgrade.
|
||||
|
||||
The [commandline](https://xrpl.org/docs/references/http-websocket-apis/api-conventions/request-formatting/#commandline-format) always uses the latest API version. The command line is intended for ad-hoc usage by humans, not programs or automated scripts. The command line is not meant for use in production code.
|
||||
|
||||
For a log of breaking changes, see the **API Version [number]** headings. In general, breaking changes are associated with a particular API Version number. For non-breaking changes, scroll to the **XRP Ledger version [x.y.z]** headings. Non-breaking changes are associated with a particular XRP Ledger (`rippled`) release.
|
||||
|
||||
## API Version 3 (Beta)
|
||||
|
||||
API version 3 is currently a beta API. It requires enabling `[beta_rpc_api]` in the rippled configuration to use. See [API-VERSION-3.md](API-VERSION-3.md) for the full list of changes in API version 3.
|
||||
|
||||
## API Version 2
|
||||
|
||||
API version 2 is available in `rippled` version 2.0.0 and later. To use this API, clients specify `"api_version" : 2` in each request.
|
||||
|
||||
#### Removed methods
|
||||
|
||||
In API version 2, the following deprecated methods are no longer available: (https://github.com/XRPLF/rippled/pull/4759)
|
||||
|
||||
- `tx_history` - Instead, use other methods such as `account_tx` or `ledger` with the `transactions` field set to `true`.
|
||||
- `ledger_header` - Instead, use the `ledger` method.
|
||||
|
||||
#### Modifications to JSON transaction element in V2
|
||||
|
||||
In API version 2, JSON elements for transaction output have been changed and made consistent for all methods which output transactions. (https://github.com/XRPLF/rippled/pull/4775)
|
||||
This helps to unify the JSON serialization format of transactions. (https://github.com/XRPLF/clio/issues/722, https://github.com/XRPLF/rippled/issues/4727)
|
||||
|
||||
- JSON transaction element is named `tx_json`
|
||||
- Binary transaction element is named `tx_blob`
|
||||
- JSON transaction metadata element is named `meta`
|
||||
- Binary transaction metadata element is named `meta_blob`
|
||||
|
||||
Additionally, these elements are now consistently available next to `tx_json` (i.e. sibling elements), where possible:
|
||||
|
||||
- `hash` - Transaction ID. This data was stored inside transaction output in API version 1, but in API version 2 is a sibling element.
|
||||
- `ledger_index` - Ledger index (only set on validated ledgers)
|
||||
- `ledger_hash` - Ledger hash (only set on closed or validated ledgers)
|
||||
- `close_time_iso` - Ledger close time expressed in ISO 8601 time format (only set on validated ledgers)
|
||||
- `validated` - Bool element set to `true` if the transaction is in a validated ledger, otherwise `false`
|
||||
|
||||
This change affects the following methods:
|
||||
|
||||
- `tx` - Transaction data moved into element `tx_json` (was inline inside `result`) or, if binary output was requested, moved from `tx` to `tx_blob`. Renamed binary transaction metadata element (if it was requested) from `meta` to `meta_blob`. Changed location of `hash` and added new elements
|
||||
- `account_tx` - Renamed transaction element from `tx` to `tx_json`. Renamed binary transaction metadata element (if it was requested) from `meta` to `meta_blob`. Changed location of `hash` and added new elements
|
||||
- `transaction_entry` - Renamed transaction metadata element from `metadata` to `meta`. Changed location of `hash` and added new elements
|
||||
- `subscribe` - Renamed transaction element from `transaction` to `tx_json`. Changed location of `hash` and added new elements
|
||||
- `sign`, `sign_for`, `submit` and `submit_multisigned` - Changed location of `hash` element.
|
||||
|
||||
#### Modification to `Payment` transaction JSON schema
|
||||
|
||||
When reading Payments, the `Amount` field should generally **not** be used. Instead, use [delivered_amount](https://xrpl.org/partial-payments.html#the-delivered_amount-field) to see the amount that the Payment delivered. To clarify its meaning, the `Amount` field is being renamed to `DeliverMax`. (https://github.com/XRPLF/rippled/pull/4733)
|
||||
|
||||
- In `Payment` transaction type, JSON RPC field `Amount` is renamed to `DeliverMax`. To enable smooth client transition, `Amount` is still handled, as described below: (https://github.com/XRPLF/rippled/pull/4733)
|
||||
- On JSON RPC input (e.g. `submit_multisigned` etc. methods), `Amount` is recognized as an alias to `DeliverMax` for both API version 1 and version 2 clients.
|
||||
- On JSON RPC input, submitting both `Amount` and `DeliverMax` fields is allowed _only_ if they are identical; otherwise such input is rejected with `rpcINVALID_PARAMS` error.
|
||||
- On JSON RPC output (e.g. `subscribe`, `account_tx` etc. methods), `DeliverMax` is present in both API version 1 and version 2.
|
||||
- On JSON RPC output, `Amount` is only present in API version 1 and _not_ in version 2.
|
||||
|
||||
#### Modifications to account_info response
|
||||
|
||||
- `signer_lists` is returned in the root of the response. (In API version 1, it was nested under `account_data`.) (https://github.com/XRPLF/rippled/pull/3770)
|
||||
- When using an invalid `signer_lists` value, the API now returns an "invalidParams" error. (https://github.com/XRPLF/rippled/pull/4585)
|
||||
- (`signer_lists` must be a boolean. In API version 1, strings were accepted and may return a normal response - i.e. as if `signer_lists` were `true`.)
|
||||
|
||||
#### Modifications to [account_tx](https://xrpl.org/account_tx.html#account_tx) response
|
||||
|
||||
- Using `ledger_index_min`, `ledger_index_max`, and `ledger_index` returns `invalidParams` because if you use `ledger_index_min` or `ledger_index_max`, then it does not make sense to also specify `ledger_index`. In API version 1, no error was returned. (https://github.com/XRPLF/rippled/pull/4571)
|
||||
- The same applies for `ledger_index_min`, `ledger_index_max`, and `ledger_hash`. (https://github.com/XRPLF/rippled/issues/4545#issuecomment-1565065579)
|
||||
- Using a `ledger_index_min` or `ledger_index_max` beyond the range of ledgers that the server has:
|
||||
- returns `lgrIdxMalformed` in API version 2. Previously, in API version 1, no error was returned. (https://github.com/XRPLF/rippled/issues/4288)
|
||||
- Attempting to use a non-boolean value (such as a string) for the `binary` or `forward` parameters returns `invalidParams` (`rpcINVALID_PARAMS`). Previously, in API version 1, no error was returned. (https://github.com/XRPLF/rippled/pull/4620)
|
||||
|
||||
#### Modifications to [noripple_check](https://xrpl.org/noripple_check.html#noripple_check) response
|
||||
|
||||
- Attempting to use a non-boolean value (such as a string) for the `transactions` parameter returns `invalidParams` (`rpcINVALID_PARAMS`). Previously, in API version 1, no error was returned. (https://github.com/XRPLF/rippled/pull/4620)
|
||||
API version 2 is available in `rippled` version 2.0.0 and later. See [API-VERSION-2.md](API-VERSION-2.md) for the full list of changes in API version 2.
|
||||
|
||||
## API Version 1
|
||||
|
||||
This version is supported by all `rippled` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified.
|
||||
|
||||
The [commandline](https://xrpl.org/docs/references/http-websocket-apis/api-conventions/request-formatting/#commandline-format) always uses the latest API version. The command line is intended for ad-hoc usage by humans, not programs or automated scripts. The command line is not meant for use in production code.
|
||||
## XRP Ledger server version 3.1.0
|
||||
|
||||
### Inconsistency: server_info - network_id
|
||||
[Version 3.1.0](https://github.com/XRPLF/rippled/releases/tag/3.1.0) was released on Jan 27, 2026.
|
||||
|
||||
The `network_id` field was added in the `server_info` response in version 1.5.0 (2019), but it is not returned in [reporting mode](https://xrpl.org/rippled-server-modes.html#reporting-mode). However, use of reporting mode is now discouraged, in favor of using [Clio](https://github.com/XRPLF/clio) instead.
|
||||
### Additions in 3.1.0
|
||||
|
||||
- `vault_info`: New RPC method to retrieve information about a specific vault (part of XLS-66 Lending Protocol). ([#6156](https://github.com/XRPLF/rippled/pull/6156))
|
||||
|
||||
## XRP Ledger server version 3.0.0
|
||||
|
||||
[Version 3.0.0](https://github.com/XRPLF/rippled/releases/tag/3.0.0) was released on Dec 9, 2025.
|
||||
|
||||
### Additions in 3.0.0
|
||||
|
||||
- `ledger_entry`: Supports all ledger entry types with dedicated parsers. ([#5237](https://github.com/XRPLF/rippled/pull/5237))
|
||||
- `ledger_entry`: New error codes `entryNotFound` and `unexpectedLedgerType` for more specific error handling. ([#5237](https://github.com/XRPLF/rippled/pull/5237))
|
||||
- `ledger_entry`: Improved error messages with more context (e.g., specifying which field is invalid or missing). ([#5237](https://github.com/XRPLF/rippled/pull/5237))
|
||||
- `ledger_entry`: Assorted bug fixes in RPC processing. ([#5237](https://github.com/XRPLF/rippled/pull/5237))
|
||||
- `simulate`: Supports additional metadata in the response. ([#5754](https://github.com/XRPLF/rippled/pull/5754))
|
||||
|
||||
## XRP Ledger server version 2.6.2
|
||||
|
||||
[Version 2.6.2](https://github.com/XRPLF/rippled/releases/tag/2.6.2) was released on Nov 19, 2025.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## XRP Ledger server version 2.6.1
|
||||
|
||||
[Version 2.6.1](https://github.com/XRPLF/rippled/releases/tag/2.6.1) was released on Sep 30, 2025.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## XRP Ledger server version 2.6.0
|
||||
|
||||
[Version 2.6.0](https://github.com/XRPLF/rippled/releases/tag/2.6.0) was released on Aug 27, 2025.
|
||||
|
||||
### Additions in 2.6.0
|
||||
|
||||
- `account_info`: Added `allowTrustLineLocking` flag in response. ([#5525](https://github.com/XRPLF/rippled/pull/5525))
|
||||
- `ledger`: Removed the type filter from the RPC command. ([#4934](https://github.com/XRPLF/rippled/pull/4934))
|
||||
- `subscribe` (`validations` stream): `network_id` is now included. ([#5579](https://github.com/XRPLF/rippled/pull/5579))
|
||||
- `subscribe` (`transactions` stream): `nftoken_id`, `nftoken_ids`, and `offer_id` are now included in transaction metadata. ([#5230](https://github.com/XRPLF/rippled/pull/5230))
|
||||
|
||||
## XRP Ledger server version 2.5.1
|
||||
|
||||
[Version 2.5.1](https://github.com/XRPLF/rippled/releases/tag/2.5.1) was released on Sep 17, 2025.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## XRP Ledger server version 2.5.0
|
||||
|
||||
As of 2025-04-04, version 2.5.0 is in development. You can use a pre-release version by building from source or [using the `nightly` package](https://xrpl.org/docs/infrastructure/installation/install-rippled-on-ubuntu).
|
||||
[Version 2.5.0](https://github.com/XRPLF/rippled/releases/tag/2.5.0) was released on Jun 24, 2025.
|
||||
|
||||
### Additions and bugfixes in 2.5.0
|
||||
|
||||
- `channel_authorize`: If `signing_support` is not enabled in the config, the RPC is disabled.
|
||||
- `tx`: Added `ctid` field to the response and improved error handling. ([#4738](https://github.com/XRPLF/rippled/pull/4738))
|
||||
- `ledger_entry`: Improved error messages in `permissioned_domain`. ([#5344](https://github.com/XRPLF/rippled/pull/5344))
|
||||
- `simulate`: Improved multi-sign usage. ([#5479](https://github.com/XRPLF/rippled/pull/5479))
|
||||
- `channel_authorize`: If `signing_support` is not enabled in the config, the RPC is disabled. ([#5385](https://github.com/XRPLF/rippled/pull/5385))
|
||||
- `subscribe` (admin): Removed webhook queue limit to prevent dropping notifications; reduced HTTP timeout from 10 minutes to 30 seconds. ([#5163](https://github.com/XRPLF/rippled/pull/5163))
|
||||
- `ledger_data` (gRPC): Fixed crashing issue with some invalid markers. ([#5137](https://github.com/XRPLF/rippled/pull/5137))
|
||||
- `account_lines`: Fixed error with `no_ripple` and `no_ripple_peer` sometimes showing up incorrectly. ([#5345](https://github.com/XRPLF/rippled/pull/5345))
|
||||
- `account_tx`: Fixed issue with incorrect CTIDs. ([#5408](https://github.com/XRPLF/rippled/pull/5408))
|
||||
|
||||
## XRP Ledger server version 2.4.0
|
||||
|
||||
@@ -97,11 +92,19 @@ As of 2025-04-04, version 2.5.0 is in development. You can use a pre-release ver
|
||||
|
||||
### Additions and bugfixes in 2.4.0
|
||||
|
||||
- `ledger_entry`: `state` is added an alias for `ripple_state`.
|
||||
- `ledger_entry`: Enables case-insensitive filtering by canonical name in addition to case-sensitive filtering by RPC name.
|
||||
- `validators`: Added new field `validator_list_threshold` in response.
|
||||
- `simulate`: A new RPC that executes a [dry run of a transaction submission](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0069d-simulate#2-rpc-simulate)
|
||||
- Signing methods autofill fees better and properly handle transactions that don't have a base fee, and will also autofill the `NetworkID` field.
|
||||
- `simulate`: A new RPC that executes a [dry run of a transaction submission](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0069d-simulate#2-rpc-simulate). ([#5069](https://github.com/XRPLF/rippled/pull/5069))
|
||||
- Signing methods (`sign`, `sign_for`, `submit`): Autofill fees better, properly handle transactions without a base fee, and autofill the `NetworkID` field. ([#5069](https://github.com/XRPLF/rippled/pull/5069))
|
||||
- `ledger_entry`: `state` is added as an alias for `ripple_state`. ([#5199](https://github.com/XRPLF/rippled/pull/5199))
|
||||
- `ledger`, `ledger_data`, `account_objects`: Support filtering ledger entry types by their canonical names (case-insensitive). ([#5271](https://github.com/XRPLF/rippled/pull/5271))
|
||||
- `validators`: Added new field `validator_list_threshold` in response. ([#5112](https://github.com/XRPLF/rippled/pull/5112))
|
||||
- `server_info`: Added git commit hash info on admin connection. ([#5225](https://github.com/XRPLF/rippled/pull/5225))
|
||||
- `server_definitions`: Changed larger `UInt` serialized types to `Hash`. ([#5231](https://github.com/XRPLF/rippled/pull/5231))
|
||||
|
||||
## XRP Ledger server version 2.3.1
|
||||
|
||||
[Version 2.3.1](https://github.com/XRPLF/rippled/releases/tag/2.3.1) was released on Jan 29, 2025.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## XRP Ledger server version 2.3.0
|
||||
|
||||
@@ -109,19 +112,30 @@ As of 2025-04-04, version 2.5.0 is in development. You can use a pre-release ver
|
||||
|
||||
### Breaking changes in 2.3.0
|
||||
|
||||
- `book_changes`: If the requested ledger version is not available on this node, a `ledgerNotFound` error is returned and the node does not attempt to acquire the ledger from the p2p network (as with other non-admin RPCs).
|
||||
|
||||
Admins can still attempt to retrieve old ledgers with the `ledger_request` RPC.
|
||||
- `book_changes`: If the requested ledger version is not available on this node, a `ledgerNotFound` error is returned and the node does not attempt to acquire the ledger from the p2p network (as with other non-admin RPCs). Admins can still attempt to retrieve old ledgers with the `ledger_request` RPC.
|
||||
|
||||
### Additions and bugfixes in 2.3.0
|
||||
|
||||
- `book_changes`: Returns a `validated` field in its response, which was missing in prior versions.
|
||||
- `book_changes`: Returns a `validated` field in its response. ([#5096](https://github.com/XRPLF/rippled/pull/5096))
|
||||
- `book_changes`: Accepts shortcut strings (`current`, `closed`, `validated`) for the `ledger_index` parameter. ([#5096](https://github.com/XRPLF/rippled/pull/5096))
|
||||
- `server_definitions`: Include `index` in response. ([#5190](https://github.com/XRPLF/rippled/pull/5190))
|
||||
- `account_nfts`: Fix issue where unassociated marker would return incorrect results. ([#5045](https://github.com/XRPLF/rippled/pull/5045))
|
||||
- `account_objects`: Fix issue where invalid marker would not return an error. ([#5046](https://github.com/XRPLF/rippled/pull/5046))
|
||||
- `account_objects`: Disallow filtering by ledger entry types that an account cannot hold. ([#5056](https://github.com/XRPLF/rippled/pull/5056))
|
||||
- `tx`: Allow lowercase CTID. ([#5049](https://github.com/XRPLF/rippled/pull/5049))
|
||||
- `feature`: Better error handling for invalid values of `feature`. ([#5063](https://github.com/XRPLF/rippled/pull/5063))
|
||||
|
||||
## XRP Ledger server version 2.2.0
|
||||
|
||||
[Version 2.2.0](https://github.com/XRPLF/rippled/releases/tag/2.2.0) was released on Jun 5, 2024. The following additions are non-breaking (because they are purely additive):
|
||||
|
||||
- The `feature` method now has a non-admin mode for users. (It was previously only available to admin connections.) The method returns an updated list of amendments, including their names and other information. ([#4781](https://github.com/XRPLF/rippled/pull/4781))
|
||||
- `feature`: Add a non-admin mode for users. (It was previously only available to admin connections.) The method returns an updated list of amendments, including their names and other information. ([#4781](https://github.com/XRPLF/rippled/pull/4781))
|
||||
|
||||
## XRP Ledger server version 2.0.1
|
||||
|
||||
[Version 2.0.1](https://github.com/XRPLF/rippled/releases/tag/2.0.1) was released on Jan 29, 2024. The following additions are non-breaking:
|
||||
|
||||
- `path_find`: Fixes unbounded memory growth. ([#4822](https://github.com/XRPLF/rippled/pull/4822))
|
||||
|
||||
## XRP Ledger server version 2.0.0
|
||||
|
||||
@@ -129,24 +143,18 @@ Admins can still attempt to retrieve old ledgers with the `ledger_request` RPC.
|
||||
|
||||
- `server_definitions`: A new RPC that generates a `definitions.json`-like output that can be used in XRPL libraries.
|
||||
- In `Payment` transactions, `DeliverMax` has been added. This is a replacement for the `Amount` field, which should not be used. Typically, the `delivered_amount` (in transaction metadata) should be used. To ease the transition, `DeliverMax` is present regardless of API version, since adding a field is non-breaking.
|
||||
- API version 2 has been moved from beta to supported, meaning that it is generally available (regardless of the `beta_rpc_api` setting).
|
||||
|
||||
## XRP Ledger server version 2.2.0
|
||||
|
||||
The following is a non-breaking addition to the API.
|
||||
|
||||
- The `feature` method now has a non-admin mode for users. (It was previously only available to admin connections.) The method returns an updated list of amendments, including their names and other information. ([#4781](https://github.com/XRPLF/rippled/pull/4781))
|
||||
- API version 2 has been moved from beta to supported, meaning that it is generally available (regardless of the `beta_rpc_api` setting). The full list of changes is in [API-VERSION-2.md](API-VERSION-2.md).
|
||||
|
||||
## XRP Ledger server version 1.12.0
|
||||
|
||||
[Version 1.12.0](https://github.com/XRPLF/rippled/releases/tag/1.12.0) was released on Sep 6, 2023. The following additions are non-breaking (because they are purely additive).
|
||||
[Version 1.12.0](https://github.com/XRPLF/rippled/releases/tag/1.12.0) was released on Sep 6, 2023. The following additions are non-breaking (because they are purely additive):
|
||||
|
||||
- `server_info`: Added `ports`, an array which advertises the RPC and WebSocket ports. This information is also included in the `/crawl` endpoint (which calls `server_info` internally). `grpc` and `peer` ports are also included. (https://github.com/XRPLF/rippled/pull/4427)
|
||||
- `server_info`: Added `ports`, an array which advertises the RPC and WebSocket ports. This information is also included in the `/crawl` endpoint (which calls `server_info` internally). `grpc` and `peer` ports are also included. ([#4427](https://github.com/XRPLF/rippled/pull/4427))
|
||||
- `ports` contains objects, each containing a `port` for the listening port (a number string), and a `protocol` array listing the supported protocols on that port.
|
||||
- This allows crawlers to build a more detailed topology without needing to port-scan nodes.
|
||||
- (For peers and other non-admin clients, the info about admin ports is excluded.)
|
||||
- Clawback: The following additions are gated by the Clawback amendment (`featureClawback`). (https://github.com/XRPLF/rippled/pull/4553)
|
||||
- Adds an [AccountRoot flag](https://xrpl.org/accountroot.html#accountroot-flags) called `lsfAllowTrustLineClawback` (https://github.com/XRPLF/rippled/pull/4617)
|
||||
- Clawback: The following additions are gated by the Clawback amendment (`featureClawback`). ([#4553](https://github.com/XRPLF/rippled/pull/4553))
|
||||
- Adds an [AccountRoot flag](https://xrpl.org/accountroot.html#accountroot-flags) called `lsfAllowTrustLineClawback`. ([#4617](https://github.com/XRPLF/rippled/pull/4617))
|
||||
- Adds the corresponding `asfAllowTrustLineClawback` [AccountSet Flag](https://xrpl.org/accountset.html#accountset-flags) as well.
|
||||
- Clawback is disabled by default, so if an issuer desires the ability to claw back funds, they must use an `AccountSet` transaction to set the AllowTrustLineClawback flag. They must do this before creating any trust lines, offers, escrows, payment channels, or checks.
|
||||
- Adds the [Clawback transaction type](https://github.com/XRPLF/XRPL-Standards/blob/master/XLS-39d-clawback/README.md#331-clawback-transaction), containing these fields:
|
||||
@@ -181,16 +189,16 @@ The following is a non-breaking addition to the API.
|
||||
|
||||
### Breaking changes in 1.11
|
||||
|
||||
- Added the ability to mark amendments as obsolete. For the `feature` admin API, there is a new possible value for the `vetoed` field. (https://github.com/XRPLF/rippled/pull/4291)
|
||||
- Added the ability to mark amendments as obsolete. For the `feature` admin API, there is a new possible value for the `vetoed` field. ([#4291](https://github.com/XRPLF/rippled/pull/4291))
|
||||
- The value of `vetoed` can now be `true`, `false`, or `"Obsolete"`.
|
||||
- Removed the acceptance of seeds or public keys in place of account addresses. (https://github.com/XRPLF/rippled/pull/4404)
|
||||
- Removed the acceptance of seeds or public keys in place of account addresses. ([#4404](https://github.com/XRPLF/rippled/pull/4404))
|
||||
- This simplifies the API and encourages better security practices (i.e. seeds should never be sent over the network).
|
||||
- For the `ledger_data` method, when all entries are filtered out, the `state` field of the response is now an empty list (in other words, an empty array, `[]`). (Previously, it would return `null`.) While this is technically a breaking change, the new behavior is consistent with the documentation, so this is considered only a bug fix. (https://github.com/XRPLF/rippled/pull/4398)
|
||||
- For the `ledger_data` method, when all entries are filtered out, the `state` field of the response is now an empty list (in other words, an empty array, `[]`). (Previously, it would return `null`.) While this is technically a breaking change, the new behavior is consistent with the documentation, so this is considered only a bug fix. ([#4398](https://github.com/XRPLF/rippled/pull/4398))
|
||||
- If and when the `fixNFTokenRemint` amendment activates, there will be a new AccountRoot field, `FirstNFTSequence`. This field is set to the current account sequence when the account issues their first NFT. If an account has not issued any NFTs, then the field is not set. ([#4406](https://github.com/XRPLF/rippled/pull/4406))
|
||||
- There is a new account deletion restriction: an account can only be deleted if `FirstNFTSequence` + `MintedNFTokens` + `256` is less than the current ledger sequence.
|
||||
- This is potentially a breaking change if clients have logic for determining whether an account can be deleted.
|
||||
- NetworkID
|
||||
- For sidechains and networks with a network ID greater than 1024, there is a new [transaction common field](https://xrpl.org/transaction-common-fields.html), `NetworkID`. (https://github.com/XRPLF/rippled/pull/4370)
|
||||
- For sidechains and networks with a network ID greater than 1024, there is a new [transaction common field](https://xrpl.org/transaction-common-fields.html), `NetworkID`. ([#4370](https://github.com/XRPLF/rippled/pull/4370))
|
||||
- This field helps to prevent replay attacks and is now required for chains whose network ID is 1025 or higher.
|
||||
- The field must be omitted for Mainnet, so there is no change for Mainnet users.
|
||||
- There are three new local error codes:
|
||||
@@ -200,10 +208,10 @@ The following is a non-breaking addition to the API.
|
||||
|
||||
### Additions and bug fixes in 1.11
|
||||
|
||||
- Added `nftoken_id`, `nftoken_ids` and `offer_id` meta fields into NFT `tx` and `account_tx` responses. (https://github.com/XRPLF/rippled/pull/4447)
|
||||
- Added an `account_flags` object to the `account_info` method response. (https://github.com/XRPLF/rippled/pull/4459)
|
||||
- Added `NFTokenPages` to the `account_objects` RPC. (https://github.com/XRPLF/rippled/pull/4352)
|
||||
- Fixed: `marker` returned from the `account_lines` command would not work on subsequent commands. (https://github.com/XRPLF/rippled/pull/4361)
|
||||
- Added `nftoken_id`, `nftoken_ids` and `offer_id` meta fields into NFT `tx` and `account_tx` responses. ([#4447](https://github.com/XRPLF/rippled/pull/4447))
|
||||
- Added an `account_flags` object to the `account_info` method response. ([#4459](https://github.com/XRPLF/rippled/pull/4459))
|
||||
- Added `NFTokenPages` to the `account_objects` RPC. ([#4352](https://github.com/XRPLF/rippled/pull/4352))
|
||||
- Fixed: `marker` returned from the `account_lines` command would not work on subsequent commands. ([#4361](https://github.com/XRPLF/rippled/pull/4361))
|
||||
|
||||
## XRP Ledger server version 1.10.0
|
||||
|
||||
|
||||
66
API-VERSION-2.md
Normal file
66
API-VERSION-2.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# API Version 2
|
||||
|
||||
API version 2 is available in `rippled` version 2.0.0 and later. To use this API, clients specify `"api_version" : 2` in each request.
|
||||
|
||||
For info about how [API versioning](https://xrpl.org/request-formatting.html#api-versioning) works, including examples, please view the [XLS-22d spec](https://github.com/XRPLF/XRPL-Standards/discussions/54). For details about the implementation of API versioning, view the [implementation PR](https://github.com/XRPLF/rippled/pull/3155). API versioning ensures existing integrations and users continue to receive existing behavior, while those that request a higher API version will experience new behavior.
|
||||
|
||||
## Removed methods
|
||||
|
||||
In API version 2, the following deprecated methods are no longer available: ([#4759](https://github.com/XRPLF/rippled/pull/4759))
|
||||
|
||||
- `tx_history` - Instead, use other methods such as `account_tx` or `ledger` with the `transactions` field set to `true`.
|
||||
- `ledger_header` - Instead, use the `ledger` method.
|
||||
|
||||
## Modifications to JSON transaction element in API version 2
|
||||
|
||||
In API version 2, JSON elements for transaction output have been changed and made consistent for all methods which output transactions. ([#4775](https://github.com/XRPLF/rippled/pull/4775))
|
||||
This helps to unify the JSON serialization format of transactions. ([clio#722](https://github.com/XRPLF/clio/issues/722), [#4727](https://github.com/XRPLF/rippled/issues/4727))
|
||||
|
||||
- JSON transaction element is named `tx_json`
|
||||
- Binary transaction element is named `tx_blob`
|
||||
- JSON transaction metadata element is named `meta`
|
||||
- Binary transaction metadata element is named `meta_blob`
|
||||
|
||||
Additionally, these elements are now consistently available next to `tx_json` (i.e. sibling elements), where possible:
|
||||
|
||||
- `hash` - Transaction ID. This data was stored inside transaction output in API version 1, but in API version 2 is a sibling element.
|
||||
- `ledger_index` - Ledger index (only set on validated ledgers)
|
||||
- `ledger_hash` - Ledger hash (only set on closed or validated ledgers)
|
||||
- `close_time_iso` - Ledger close time expressed in ISO 8601 time format (only set on validated ledgers)
|
||||
- `validated` - Bool element set to `true` if the transaction is in a validated ledger, otherwise `false`
|
||||
|
||||
This change affects the following methods:
|
||||
|
||||
- `tx` - Transaction data moved into element `tx_json` (was inline inside `result`) or, if binary output was requested, moved from `tx` to `tx_blob`. Renamed binary transaction metadata element (if it was requested) from `meta` to `meta_blob`. Changed location of `hash` and added new elements
|
||||
- `account_tx` - Renamed transaction element from `tx` to `tx_json`. Renamed binary transaction metadata element (if it was requested) from `meta` to `meta_blob`. Changed location of `hash` and added new elements
|
||||
- `transaction_entry` - Renamed transaction metadata element from `metadata` to `meta`. Changed location of `hash` and added new elements
|
||||
- `subscribe` - Renamed transaction element from `transaction` to `tx_json`. Changed location of `hash` and added new elements
|
||||
- `sign`, `sign_for`, `submit` and `submit_multisigned` - Changed location of `hash` element.
|
||||
|
||||
## Modifications to `Payment` transaction JSON schema
|
||||
|
||||
When reading Payments, the `Amount` field should generally **not** be used. Instead, use [delivered_amount](https://xrpl.org/partial-payments.html#the-delivered_amount-field) to see the amount that the Payment delivered. To clarify its meaning, the `Amount` field is being renamed to `DeliverMax`. ([#4733](https://github.com/XRPLF/rippled/pull/4733))
|
||||
|
||||
- In `Payment` transaction type, JSON RPC field `Amount` is renamed to `DeliverMax`. To enable smooth client transition, `Amount` is still handled, as described below: ([#4733](https://github.com/XRPLF/rippled/pull/4733))
|
||||
- On JSON RPC input (e.g. `submit_multisigned` etc. methods), `Amount` is recognized as an alias to `DeliverMax` for both API version 1 and version 2 clients.
|
||||
- On JSON RPC input, submitting both `Amount` and `DeliverMax` fields is allowed _only_ if they are identical; otherwise such input is rejected with `rpcINVALID_PARAMS` error.
|
||||
- On JSON RPC output (e.g. `subscribe`, `account_tx` etc. methods), `DeliverMax` is present in both API version 1 and version 2.
|
||||
- On JSON RPC output, `Amount` is only present in API version 1 and _not_ in version 2.
|
||||
|
||||
## Modifications to account_info response
|
||||
|
||||
- `signer_lists` is returned in the root of the response. (In API version 1, it was nested under `account_data`.) ([#3770](https://github.com/XRPLF/rippled/pull/3770))
|
||||
- When using an invalid `signer_lists` value, the API now returns an "invalidParams" error. ([#4585](https://github.com/XRPLF/rippled/pull/4585))
|
||||
- (`signer_lists` must be a boolean. In API version 1, strings were accepted and may return a normal response - i.e. as if `signer_lists` were `true`.)
|
||||
|
||||
## Modifications to [account_tx](https://xrpl.org/account_tx.html#account_tx) response
|
||||
|
||||
- Using `ledger_index_min`, `ledger_index_max`, and `ledger_index` returns `invalidParams` because if you use `ledger_index_min` or `ledger_index_max`, then it does not make sense to also specify `ledger_index`. In API version 1, no error was returned. ([#4571](https://github.com/XRPLF/rippled/pull/4571))
|
||||
- The same applies for `ledger_index_min`, `ledger_index_max`, and `ledger_hash`. ([#4545](https://github.com/XRPLF/rippled/issues/4545#issuecomment-1565065579))
|
||||
- Using a `ledger_index_min` or `ledger_index_max` beyond the range of ledgers that the server has:
|
||||
- returns `lgrIdxMalformed` in API version 2. Previously, in API version 1, no error was returned. ([#4288](https://github.com/XRPLF/rippled/issues/4288))
|
||||
- Attempting to use a non-boolean value (such as a string) for the `binary` or `forward` parameters returns `invalidParams` (`rpcINVALID_PARAMS`). Previously, in API version 1, no error was returned. ([#4620](https://github.com/XRPLF/rippled/pull/4620))
|
||||
|
||||
## Modifications to [noripple_check](https://xrpl.org/noripple_check.html#noripple_check) response
|
||||
|
||||
- Attempting to use a non-boolean value (such as a string) for the `transactions` parameter returns `invalidParams` (`rpcINVALID_PARAMS`). Previously, in API version 1, no error was returned. ([#4620](https://github.com/XRPLF/rippled/pull/4620))
|
||||
27
API-VERSION-3.md
Normal file
27
API-VERSION-3.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# API Version 3
|
||||
|
||||
API version 3 is currently a **beta API**. It requires enabling `[beta_rpc_api]` in the rippled configuration to use. To use this API, clients specify `"api_version" : 3` in each request.
|
||||
|
||||
For info about how [API versioning](https://xrpl.org/request-formatting.html#api-versioning) works, including examples, please view the [XLS-22d spec](https://github.com/XRPLF/XRPL-Standards/discussions/54). For details about the implementation of API versioning, view the [implementation PR](https://github.com/XRPLF/rippled/pull/3155). API versioning ensures existing integrations and users continue to receive existing behavior, while those that request a higher API version will experience new behavior.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Modifications to `amm_info`
|
||||
|
||||
The order of error checks has been changed to provide more specific error messages. ([#4924](https://github.com/XRPLF/rippled/pull/4924))
|
||||
|
||||
- **Before (API v2)**: When sending an invalid account or asset to `amm_info` while other parameters are not set as expected, the method returns a generic `rpcINVALID_PARAMS` error.
|
||||
- **After (API v3)**: The same scenario returns a more specific error: `rpcISSUE_MALFORMED` for malformed assets or `rpcACT_MALFORMED` for malformed accounts.
|
||||
|
||||
### Modifications to `ledger_entry`
|
||||
|
||||
Added support for string shortcuts to look up fixed-location ledger entries using the `"index"` parameter. ([#5644](https://github.com/XRPLF/rippled/pull/5644))
|
||||
|
||||
In API version 3, the following string values can be used with the `"index"` parameter:
|
||||
|
||||
- `"index": "amendments"` - Returns the `Amendments` ledger entry
|
||||
- `"index": "fee"` - Returns the `FeeSettings` ledger entry
|
||||
- `"index": "nunl"` - Returns the `NegativeUNL` ledger entry
|
||||
- `"index": "hashes"` - Returns the "short" `LedgerHashes` ledger entry (recent ledger hashes)
|
||||
|
||||
These shortcuts are only available in API version 3 and later. In API versions 1 and 2, these string values would result in an error.
|
||||
@@ -103,6 +103,7 @@ find_package(OpenSSL REQUIRED)
|
||||
find_package(secp256k1 REQUIRED)
|
||||
find_package(SOCI REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(wasmi REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -872,7 +872,8 @@ git push --delete upstream-push master-next
|
||||
11. [Create a new release on
|
||||
Github](https://github.com/XRPLF/rippled/releases). Be sure that
|
||||
"Set as the latest release" is checked.
|
||||
12. Finally [reverse merge the release into `develop`](#follow-up-reverse-merge).
|
||||
12. Open a PR to update the [API-CHANGELOG](API-CHANGELOG.md) and `API-VERSION-[n].md` with the changes for this release (if any are missing).
|
||||
13. Finally, [reverse merge the release into `develop`](#follow-up-reverse-merge).
|
||||
|
||||
#### Special cases: point releases, hotfixes, etc.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ set(is_amd64 FALSE)
|
||||
set(is_arm64 FALSE)
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
|
||||
set(is_amd64 TRUE)
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
|
||||
set(is_arm64 TRUE)
|
||||
else ()
|
||||
message(FATAL_ERROR "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
|
||||
@@ -45,6 +45,7 @@ target_link_libraries(
|
||||
Xrpl::opts
|
||||
Xrpl::syslibs
|
||||
secp256k1::secp256k1
|
||||
wasmi::wasmi
|
||||
xrpl.libpb
|
||||
xxHash::xxhash
|
||||
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"requires": [
|
||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1765850150.075",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
|
||||
"wasmi/1.0.6#407c9db14601a8af1c7dd3b388f3e4cd%1768164779.349",
|
||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
|
||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
|
||||
|
||||
@@ -35,6 +35,7 @@ class Xrpl(ConanFile):
|
||||
"openssl/3.5.4",
|
||||
"secp256k1/0.7.0",
|
||||
"soci/4.0.3",
|
||||
"wasmi/1.0.6",
|
||||
"zlib/1.3.1",
|
||||
]
|
||||
|
||||
@@ -215,6 +216,7 @@ class Xrpl(ConanFile):
|
||||
"soci::soci",
|
||||
"secp256k1::secp256k1",
|
||||
"sqlite3::sqlite",
|
||||
"wasmi::wasmi",
|
||||
"xxhash::xxhash",
|
||||
"zlib::zlib",
|
||||
]
|
||||
|
||||
@@ -717,6 +717,10 @@ abs(Number x) noexcept
|
||||
Number
|
||||
power(Number const& f, unsigned n);
|
||||
|
||||
// logarithm with base 10
|
||||
Number
|
||||
log10(Number const& value, int iterations = 50);
|
||||
|
||||
// Returns f^(1/d)
|
||||
// Uses Newton–Raphson iterations until the result stops changing
|
||||
// to find the root of the polynomial g(x) = x^d - f
|
||||
|
||||
202
include/xrpl/core/ServiceRegistry.h
Normal file
202
include/xrpl/core/ServiceRegistry.h
Normal file
@@ -0,0 +1,202 @@
|
||||
#ifndef XRPL_CORE_SERVICEREGISTRY_H_INCLUDED
|
||||
#define XRPL_CORE_SERVICEREGISTRY_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/SHAMapHash.h>
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/ledger/CachedSLEs.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations
|
||||
namespace NodeStore {
|
||||
class Database;
|
||||
}
|
||||
namespace Resource {
|
||||
class Manager;
|
||||
}
|
||||
namespace perf {
|
||||
class PerfLog;
|
||||
}
|
||||
|
||||
class AcceptedLedger;
|
||||
class AmendmentTable;
|
||||
class Cluster;
|
||||
class CollectorManager;
|
||||
class DatabaseCon;
|
||||
class Family;
|
||||
class HashRouter;
|
||||
class InboundLedgers;
|
||||
class InboundTransactions;
|
||||
class JobQueue;
|
||||
class LedgerCleaner;
|
||||
class LedgerMaster;
|
||||
class LedgerReplayer;
|
||||
class LoadFeeTrack;
|
||||
class LoadManager;
|
||||
class ManifestCache;
|
||||
class NetworkOPs;
|
||||
class OpenLedger;
|
||||
class OrderBookDB;
|
||||
class Overlay;
|
||||
class PathRequests;
|
||||
class PeerReservationTable;
|
||||
class PendingSaves;
|
||||
class RelationalDatabase;
|
||||
class ServerHandler;
|
||||
class SHAMapStore;
|
||||
class TimeKeeper;
|
||||
class TransactionMaster;
|
||||
class TxQ;
|
||||
class ValidatorList;
|
||||
class ValidatorSite;
|
||||
|
||||
template <class Adaptor>
|
||||
class Validations;
|
||||
class RCLValidationsAdaptor;
|
||||
using RCLValidations = Validations<RCLValidationsAdaptor>;
|
||||
|
||||
using NodeCache = TaggedCache<SHAMapHash, Blob>;
|
||||
|
||||
/** Service registry for dependency injection.
|
||||
|
||||
This abstract interface provides access to various services and components
|
||||
used throughout the application. It separates the service locator pattern
|
||||
from the Application lifecycle management.
|
||||
|
||||
Components that need access to services can hold a reference to
|
||||
ServiceRegistry rather than Application when they only need service
|
||||
access and not lifecycle management.
|
||||
|
||||
*/
|
||||
class ServiceRegistry
|
||||
{
|
||||
public:
|
||||
ServiceRegistry() = default;
|
||||
virtual ~ServiceRegistry() = default;
|
||||
|
||||
// Core infrastructure services
|
||||
virtual CollectorManager&
|
||||
getCollectorManager() = 0;
|
||||
|
||||
virtual Family&
|
||||
getNodeFamily() = 0;
|
||||
|
||||
virtual TimeKeeper&
|
||||
timeKeeper() = 0;
|
||||
|
||||
virtual JobQueue&
|
||||
getJobQueue() = 0;
|
||||
|
||||
virtual NodeCache&
|
||||
getTempNodeCache() = 0;
|
||||
|
||||
virtual CachedSLEs&
|
||||
cachedSLEs() = 0;
|
||||
|
||||
// Protocol and validation services
|
||||
virtual AmendmentTable&
|
||||
getAmendmentTable() = 0;
|
||||
|
||||
virtual HashRouter&
|
||||
getHashRouter() = 0;
|
||||
|
||||
virtual LoadFeeTrack&
|
||||
getFeeTrack() = 0;
|
||||
|
||||
virtual LoadManager&
|
||||
getLoadManager() = 0;
|
||||
|
||||
virtual RCLValidations&
|
||||
getValidations() = 0;
|
||||
|
||||
virtual ValidatorList&
|
||||
validators() = 0;
|
||||
|
||||
virtual ValidatorSite&
|
||||
validatorSites() = 0;
|
||||
|
||||
virtual ManifestCache&
|
||||
validatorManifests() = 0;
|
||||
|
||||
virtual ManifestCache&
|
||||
publisherManifests() = 0;
|
||||
|
||||
// Network services
|
||||
virtual Overlay&
|
||||
overlay() = 0;
|
||||
|
||||
virtual Cluster&
|
||||
cluster() = 0;
|
||||
|
||||
virtual PeerReservationTable&
|
||||
peerReservations() = 0;
|
||||
|
||||
virtual Resource::Manager&
|
||||
getResourceManager() = 0;
|
||||
|
||||
// Storage services
|
||||
virtual NodeStore::Database&
|
||||
getNodeStore() = 0;
|
||||
|
||||
virtual SHAMapStore&
|
||||
getSHAMapStore() = 0;
|
||||
|
||||
virtual RelationalDatabase&
|
||||
getRelationalDatabase() = 0;
|
||||
|
||||
// Ledger services
|
||||
virtual InboundLedgers&
|
||||
getInboundLedgers() = 0;
|
||||
|
||||
virtual InboundTransactions&
|
||||
getInboundTransactions() = 0;
|
||||
|
||||
virtual TaggedCache<uint256, AcceptedLedger>&
|
||||
getAcceptedLedgerCache() = 0;
|
||||
|
||||
virtual LedgerMaster&
|
||||
getLedgerMaster() = 0;
|
||||
|
||||
virtual LedgerCleaner&
|
||||
getLedgerCleaner() = 0;
|
||||
|
||||
virtual LedgerReplayer&
|
||||
getLedgerReplayer() = 0;
|
||||
|
||||
virtual PendingSaves&
|
||||
pendingSaves() = 0;
|
||||
|
||||
virtual OpenLedger&
|
||||
openLedger() = 0;
|
||||
|
||||
virtual OpenLedger const&
|
||||
openLedger() const = 0;
|
||||
|
||||
// Transaction and operation services
|
||||
virtual NetworkOPs&
|
||||
getOPs() = 0;
|
||||
|
||||
virtual OrderBookDB&
|
||||
getOrderBookDB() = 0;
|
||||
|
||||
virtual TransactionMaster&
|
||||
getMasterTransaction() = 0;
|
||||
|
||||
virtual TxQ&
|
||||
getTxQ() = 0;
|
||||
|
||||
virtual PathRequests&
|
||||
getPathRequests() = 0;
|
||||
|
||||
// Server services
|
||||
virtual ServerHandler&
|
||||
getServerHandler() = 0;
|
||||
|
||||
virtual perf::PerfLog&
|
||||
getPerfLog() = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
@@ -251,6 +251,13 @@ std::uint8_t constexpr vaultMaximumIOUScale = 18;
|
||||
* another vault; counted from 0 */
|
||||
std::uint8_t constexpr maxAssetCheckDepth = 5;
|
||||
|
||||
/** The maximum length of a Data field in Escrow object that can be updated by
|
||||
* Wasm code */
|
||||
std::size_t constexpr maxWasmDataLength = 4 * 1024;
|
||||
|
||||
/** The maximum length of a parameters passed from Wasm code*/
|
||||
std::size_t constexpr maxWasmParamLength = 1024;
|
||||
|
||||
/** A ledger index. */
|
||||
using LedgerIndex = std::uint32_t;
|
||||
|
||||
|
||||
@@ -122,6 +122,8 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
|
||||
temBAD_WASM,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -578,7 +578,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
|
||||
// The unrounded true total value of the loan.
|
||||
//
|
||||
// - TrueTotalPrincipalOutstanding can be computed using the algorithm
|
||||
// in the ripple::detail::loanPrincipalFromPeriodicPayment function.
|
||||
// in the xrpl::detail::loanPrincipalFromPeriodicPayment function.
|
||||
//
|
||||
// - TrueTotalInterestOutstanding = TrueTotalLoanValue -
|
||||
// TrueTotalPrincipalOutstanding
|
||||
|
||||
@@ -925,6 +925,73 @@ power(Number const& f, unsigned n)
|
||||
return r;
|
||||
}
|
||||
|
||||
// Series expansion method approximation of ln(x)
|
||||
static Number
|
||||
ln(Number const& x, int iterations = 50)
|
||||
{
|
||||
static Number const N0(0);
|
||||
static Number const N2(2, 0);
|
||||
static Number const N05(5, -1);
|
||||
static Number const LN2(693'147'180'559'945'309ll, -18);
|
||||
|
||||
if (x <= 0)
|
||||
throw std::runtime_error("Not a positive value");
|
||||
else if (x == 1)
|
||||
return N0;
|
||||
|
||||
int exponent = 0;
|
||||
Number mantissa = x;
|
||||
|
||||
while (mantissa >= N2)
|
||||
{
|
||||
mantissa /= 2;
|
||||
exponent += 1;
|
||||
}
|
||||
while (mantissa < N05)
|
||||
{
|
||||
mantissa *= 2;
|
||||
exponent -= 1;
|
||||
}
|
||||
|
||||
Number z = (mantissa - 1) / (mantissa + 1);
|
||||
Number const zz = z * z;
|
||||
Number sum;
|
||||
|
||||
for (int i = 1; i <= iterations; ++i)
|
||||
{
|
||||
sum = sum + z / (2 * i - 1);
|
||||
z = z * zz;
|
||||
}
|
||||
|
||||
return 2 * sum + exponent * LN2;
|
||||
}
|
||||
|
||||
Number
|
||||
log10(Number const& x, int iterations)
|
||||
{
|
||||
static Number const N0(0);
|
||||
static Number const LN10(2'302'585'092'994'046ll, -15);
|
||||
|
||||
if (x <= 0)
|
||||
throw std::runtime_error("Not a positive value");
|
||||
else if (x == 1)
|
||||
return N0;
|
||||
|
||||
if (x <= Number(10))
|
||||
{
|
||||
auto const r = ln(x, iterations) / LN10;
|
||||
return r;
|
||||
}
|
||||
|
||||
// (1 <= normalX < 10)
|
||||
// ln(x) = ln(normalX * 10^norm) = ln(normalX) + norm * ln(10)
|
||||
int diffExp = 15 + x.exponent();
|
||||
Number const normalX = x / Number(1, diffExp);
|
||||
auto const lnX = ln(normalX, iterations) + diffExp * LN10;
|
||||
auto const lgX = lnX / LN10;
|
||||
return lgX;
|
||||
}
|
||||
|
||||
// Returns f^(1/d)
|
||||
// Uses Newton–Raphson iterations until the result stops changing
|
||||
// to find the non-negative root of the polynomial g(x) = x^d - f
|
||||
|
||||
@@ -198,6 +198,7 @@ transResults()
|
||||
MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."),
|
||||
MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."),
|
||||
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."),
|
||||
MAKE_ERROR(temBAD_WASM, "Malformed: Provided WASM code is invalid."),
|
||||
|
||||
MAKE_ERROR(terRETRY, "Retry transaction."),
|
||||
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),
|
||||
|
||||
2677
src/test/app/HostFuncImpl_test.cpp
Normal file
2677
src/test/app/HostFuncImpl_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -876,42 +876,48 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.fund(XRP(1000), alice, buyer, gw);
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
uint256 const nftAlice0ID = token::getNextID(env, alice, 0, tfTransferable);
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
uint8_t aliceCount = 1;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
uint256 const nftXrpOnlyID = token::getNextID(env, alice, 0, tfOnlyXRP | tfTransferable);
|
||||
env(token::mint(alice, 0), txflags(tfOnlyXRP | tfTransferable));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
uint256 nftNoXferID = token::getNextID(env, alice, 0);
|
||||
env(token::mint(alice, 0));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
// alice creates sell offers for her nfts.
|
||||
uint256 const plainOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftAlice0ID, XRP(10)), txflags(tfSellNFToken));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
uint256 const audOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftAlice0ID, gwAUD(30)), txflags(tfSellNFToken));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 3);
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
uint256 const xrpOnlyOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftXrpOnlyID, XRP(20)), txflags(tfSellNFToken));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 4);
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
uint256 const noXferOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftNoXferID, XRP(30)), txflags(tfSellNFToken));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 5);
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
// alice creates a sell offer that will expire soon.
|
||||
uint256 const aliceExpOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
@@ -919,7 +925,17 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
txflags(tfSellNFToken),
|
||||
token::expiration(lastClose(env) + 5));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 6);
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
// buyer creates a Buy offer that will expire soon.
|
||||
uint256 const buyerExpOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftAlice0ID, XRP(40)),
|
||||
token::owner(alice),
|
||||
token::expiration(lastClose(env) + 5));
|
||||
env.close();
|
||||
uint8_t buyerCount = 1;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// preflight
|
||||
@@ -927,12 +943,12 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
// Set a negative fee.
|
||||
env(token::acceptSellOffer(buyer, noXferOfferIndex), fee(STAmount(10ull, true)), ter(temBAD_FEE));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Set an invalid flag.
|
||||
env(token::acceptSellOffer(buyer, noXferOfferIndex), txflags(0x00008000), ter(temINVALID_FLAG));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Supply nether an sfNFTokenBuyOffer nor an sfNFTokenSellOffer field.
|
||||
{
|
||||
@@ -940,7 +956,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
jv.removeMember(sfNFTokenSellOffer.jsonName);
|
||||
env(jv, ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
|
||||
// A buy offer may not contain a sfNFTokenBrokerFee field.
|
||||
@@ -949,7 +965,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
jv[sfNFTokenBrokerFee.jsonName] = STAmount(500000).getJson(JsonOptions::none);
|
||||
env(jv, ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
|
||||
// A sell offer may not contain a sfNFTokenBrokerFee field.
|
||||
@@ -958,7 +974,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
jv[sfNFTokenBrokerFee.jsonName] = STAmount(500000).getJson(JsonOptions::none);
|
||||
env(jv, ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
|
||||
// A brokered offer may not contain a negative or zero brokerFee.
|
||||
@@ -966,7 +982,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
token::brokerFee(gwAUD(0)),
|
||||
ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim
|
||||
@@ -974,33 +990,48 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
// The buy offer must be non-zero.
|
||||
env(token::acceptBuyOffer(buyer, beast::zero), ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// The buy offer must be present in the ledger.
|
||||
uint256 const missingOfferIndex = keylet::nftoffer(alice, 1).key;
|
||||
env(token::acceptBuyOffer(buyer, missingOfferIndex), ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// The buy offer must not have expired.
|
||||
env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
|
||||
// NOTE: this is only a preclaim check with the
|
||||
// fixExpiredNFTokenOfferRemoval amendment disabled.
|
||||
env(token::acceptBuyOffer(alice, buyerExpOfferIndex), ter(tecEXPIRED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
buyerCount--;
|
||||
}
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// The sell offer must be non-zero.
|
||||
env(token::acceptSellOffer(buyer, beast::zero), ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// The sell offer must be present in the ledger.
|
||||
env(token::acceptSellOffer(buyer, missingOfferIndex), ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// The sell offer must not have expired.
|
||||
// NOTE: this is only a preclaim check with the
|
||||
// fixExpiredNFTokenOfferRemoval amendment disabled.
|
||||
env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
// Alice's count is decremented by one when the expired offer is
|
||||
// removed.
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
aliceCount--;
|
||||
}
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim brokered
|
||||
@@ -1012,8 +1043,13 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(pay(gw, buyer, gwAUD(30)));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
aliceCount++;
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// We're about to exercise offer brokering, so we need
|
||||
// corresponding buy and sell offers.
|
||||
@@ -1022,35 +1058,38 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const buyerOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)), token::owner(alice));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// gw attempts to broker offers that are not for the same token.
|
||||
env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex), ter(tecNFTOKEN_BUY_SELL_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// gw attempts to broker offers that are not for the same currency.
|
||||
env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex), ter(tecNFTOKEN_BUY_SELL_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// In a brokered offer, the buyer must offer greater than or
|
||||
// equal to the selling price.
|
||||
env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex), ter(tecINSUFFICIENT_PAYMENT));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Remove buyer's offer.
|
||||
env(token::cancelOffer(buyer, {buyerOfferIndex}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
{
|
||||
// buyer creates a buy offer for one of alice's nfts.
|
||||
uint256 const buyerOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)), token::owner(alice));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Broker sets their fee in a denomination other than the one
|
||||
// used by the offers
|
||||
@@ -1058,14 +1097,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
token::brokerFee(XRP(40)),
|
||||
ter(tecNFTOKEN_BUY_SELL_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Broker fee way too big.
|
||||
env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
|
||||
token::brokerFee(gwAUD(31)),
|
||||
ter(tecINSUFFICIENT_PAYMENT));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Broker fee is smaller, but still too big once the offer
|
||||
// seller's minimum is taken into account.
|
||||
@@ -1073,12 +1112,13 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
token::brokerFee(gwAUD(1.5)),
|
||||
ter(tecINSUFFICIENT_PAYMENT));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Remove buyer's offer.
|
||||
env(token::cancelOffer(buyer, {buyerOfferIndex}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim buy
|
||||
@@ -1087,17 +1127,18 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const buyerOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)), token::owner(alice));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Don't accept a buy offer if the sell flag is set.
|
||||
env(token::acceptBuyOffer(buyer, plainOfferIndex), ter(tecNFTOKEN_OFFER_TYPE_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
// An account can't accept its own offer.
|
||||
env(token::acceptBuyOffer(buyer, buyerOfferIndex), ter(tecCANT_ACCEPT_OWN_NFTOKEN_OFFER));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// An offer acceptor must have enough funds to pay for the offer.
|
||||
env(pay(buyer, gw, gwAUD(30)));
|
||||
@@ -1105,7 +1146,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
|
||||
env(token::acceptBuyOffer(alice, buyerOfferIndex), ter(tecINSUFFICIENT_FUNDS));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// alice gives her NFT to gw, so alice no longer owns nftAlice0.
|
||||
{
|
||||
@@ -1114,7 +1155,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(token::acceptSellOffer(gw, offerIndex));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
}
|
||||
env(pay(gw, buyer, gwAUD(30)));
|
||||
env.close();
|
||||
@@ -1122,12 +1163,13 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
// alice can't accept a buy offer for an NFT she no longer owns.
|
||||
env(token::acceptBuyOffer(alice, buyerOfferIndex), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Remove buyer's offer.
|
||||
env(token::cancelOffer(buyer, {buyerOfferIndex}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim sell
|
||||
@@ -1136,23 +1178,24 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const buyerOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)), token::owner(alice));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Don't accept a sell offer without the sell flag set.
|
||||
env(token::acceptSellOffer(alice, buyerOfferIndex), ter(tecNFTOKEN_OFFER_TYPE_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
// An account can't accept its own offer.
|
||||
env(token::acceptSellOffer(alice, plainOfferIndex), ter(tecCANT_ACCEPT_OWN_NFTOKEN_OFFER));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// The seller must currently be in possession of the token they
|
||||
// are selling. alice gave nftAlice0ID to gw.
|
||||
env(token::acceptSellOffer(buyer, plainOfferIndex), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// gw gives nftAlice0ID back to alice. That allows us to check
|
||||
// buyer attempting to accept one of alice's offers with
|
||||
@@ -1163,14 +1206,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(token::acceptSellOffer(alice, offerIndex));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
}
|
||||
env(pay(buyer, gw, gwAUD(30)));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
|
||||
env(token::acceptSellOffer(buyer, audOfferIndex), ter(tecINSUFFICIENT_FUNDS));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -2769,6 +2812,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const nftokenID1 = token::getNextID(env, issuer, 0, tfTransferable);
|
||||
env(token::mint(minter, 0), token::issuer(issuer), txflags(tfTransferable));
|
||||
env.close();
|
||||
uint8_t issuerCount, minterCount, buyerCount;
|
||||
|
||||
// Test how adding an Expiration field to an offer affects permissions
|
||||
// for cancelling offers.
|
||||
@@ -2792,9 +2836,12 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const offerBuyerToMinter = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID0, drops(1)), token::owner(minter), token::expiration(expiration));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 3);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
issuerCount = 1;
|
||||
minterCount = 3;
|
||||
buyerCount = 1;
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Test who gets to cancel the offers. Anyone outside of the
|
||||
// offer-owner/destination pair should not be able to cancel
|
||||
@@ -2806,32 +2853,36 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env(token::cancelOffer(buyer, {offerIssuerToMinter}), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
BEAST_EXPECT(lastClose(env) < expiration);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 3);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// The offer creator can cancel their own unexpired offer.
|
||||
env(token::cancelOffer(minter, {offerMinterToAnyone}));
|
||||
minterCount--;
|
||||
|
||||
// The destination of a sell offer can cancel the NFT owner's
|
||||
// unexpired offer.
|
||||
env(token::cancelOffer(issuer, {offerMinterToIssuer}));
|
||||
minterCount--;
|
||||
|
||||
// Close enough ledgers to get past the expiration.
|
||||
while (lastClose(env) < expiration)
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Anyone can cancel expired offers.
|
||||
env(token::cancelOffer(issuer, {offerBuyerToMinter}));
|
||||
buyerCount--;
|
||||
env(token::cancelOffer(buyer, {offerIssuerToMinter}));
|
||||
issuerCount--;
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
// Show that:
|
||||
// 1. An unexpired sell offer with an expiration can be accepted.
|
||||
@@ -2844,44 +2895,70 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env(token::createOffer(minter, nftokenID0, drops(1)),
|
||||
token::expiration(expiration),
|
||||
txflags(tfSellNFToken));
|
||||
minterCount++;
|
||||
|
||||
uint256 const offer1 = keylet::nftoffer(minter, env.seq(minter)).key;
|
||||
env(token::createOffer(minter, nftokenID1, drops(1)),
|
||||
token::expiration(expiration),
|
||||
txflags(tfSellNFToken));
|
||||
minterCount++;
|
||||
env.close();
|
||||
BEAST_EXPECT(lastClose(env) < expiration);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 3);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Anyone can accept an unexpired sell offer.
|
||||
env(token::acceptSellOffer(buyer, offer0));
|
||||
minterCount--;
|
||||
buyerCount++;
|
||||
|
||||
// Close enough ledgers to get past the expiration.
|
||||
while (lastClose(env) < expiration)
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// No one can accept an expired sell offer.
|
||||
env(token::acceptSellOffer(buyer, offer1), ter(tecEXPIRED));
|
||||
env(token::acceptSellOffer(issuer, offer1), ter(tecEXPIRED));
|
||||
|
||||
// With fixExpiredNFTokenOfferRemoval amendment, the first accept
|
||||
// attempt deletes the expired offer. Without the amendment,
|
||||
// the offer remains and we can try to accept it again.
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// After amendment: offer was deleted by first accept attempt
|
||||
minterCount--;
|
||||
env(token::acceptSellOffer(issuer, offer1), ter(tecOBJECT_NOT_FOUND));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Before amendment: offer still exists, second accept also
|
||||
// fails
|
||||
env(token::acceptSellOffer(issuer, offer1), ter(tecEXPIRED));
|
||||
}
|
||||
env.close();
|
||||
|
||||
// The expired sell offer is still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
// Check if the expired sell offer behavior matches amendment status
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Anyone can cancel the expired sell offer.
|
||||
env(token::cancelOffer(issuer, {offer1}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
if (!features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// Before amendment: expired offer still exists and needs to be
|
||||
// cancelled
|
||||
env(token::cancelOffer(issuer, {offer1}));
|
||||
env.close();
|
||||
minterCount--;
|
||||
}
|
||||
// Ensure that owner counts are correct with and without the
|
||||
// amendment
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0 && issuerCount == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1 && minterCount == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1 && buyerCount == 1);
|
||||
|
||||
// Transfer nftokenID0 back to minter so we start the next test in
|
||||
// a simple place.
|
||||
@@ -2889,10 +2966,11 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env(token::createOffer(buyer, nftokenID0, XRP(0)), txflags(tfSellNFToken), token::destination(minter));
|
||||
env.close();
|
||||
env(token::acceptSellOffer(minter, offerSellBack));
|
||||
buyerCount--;
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
// Show that:
|
||||
// 1. An unexpired buy offer with an expiration can be accepted.
|
||||
@@ -2903,14 +2981,16 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
|
||||
uint256 const offer0 = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID0, drops(1)), token::owner(minter), token::expiration(expiration));
|
||||
buyerCount++;
|
||||
|
||||
uint256 const offer1 = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID1, drops(1)), token::owner(minter), token::expiration(expiration));
|
||||
buyerCount++;
|
||||
env.close();
|
||||
BEAST_EXPECT(lastClose(env) < expiration);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// An unexpired buy offer can be accepted.
|
||||
env(token::acceptBuyOffer(minter, offer0));
|
||||
@@ -2919,26 +2999,48 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
while (lastClose(env) < expiration)
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// An expired buy offer cannot be accepted.
|
||||
env(token::acceptBuyOffer(minter, offer1), ter(tecEXPIRED));
|
||||
env(token::acceptBuyOffer(issuer, offer1), ter(tecEXPIRED));
|
||||
|
||||
// With fixExpiredNFTokenOfferRemoval amendment, the first accept
|
||||
// attempt deletes the expired offer. Without the amendment,
|
||||
// the offer remains and we can try to accept it again.
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// After amendment: offer was deleted by first accept attempt
|
||||
buyerCount--;
|
||||
env(token::acceptBuyOffer(issuer, offer1), ter(tecOBJECT_NOT_FOUND));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Before amendment: offer still exists, second accept also
|
||||
// fails
|
||||
env(token::acceptBuyOffer(issuer, offer1), ter(tecEXPIRED));
|
||||
}
|
||||
env.close();
|
||||
|
||||
// The expired buy offer is still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
// Check if the expired buy offer behavior matches amendment status
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// Anyone can cancel the expired buy offer.
|
||||
env(token::cancelOffer(issuer, {offer1}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
if (!features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// Before amendment: expired offer still exists and can be
|
||||
// cancelled
|
||||
env(token::cancelOffer(issuer, {offer1}));
|
||||
env.close();
|
||||
buyerCount--;
|
||||
}
|
||||
// Ensure that owner counts are the same with and without the
|
||||
// amendment
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0 && issuerCount == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1 && minterCount == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1 && buyerCount == 1);
|
||||
|
||||
// Transfer nftokenID0 back to minter so we start the next test in
|
||||
// a simple place.
|
||||
@@ -2947,9 +3049,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(token::acceptSellOffer(minter, offerSellBack));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
// Show that in brokered mode:
|
||||
// 1. An unexpired sell offer with an expiration can be accepted.
|
||||
@@ -2962,50 +3065,74 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env(token::createOffer(minter, nftokenID0, drops(1)),
|
||||
token::expiration(expiration),
|
||||
txflags(tfSellNFToken));
|
||||
minterCount++;
|
||||
|
||||
uint256 const sellOffer1 = keylet::nftoffer(minter, env.seq(minter)).key;
|
||||
env(token::createOffer(minter, nftokenID1, drops(1)),
|
||||
token::expiration(expiration),
|
||||
txflags(tfSellNFToken));
|
||||
minterCount++;
|
||||
|
||||
uint256 const buyOffer0 = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID0, drops(1)), token::owner(minter));
|
||||
buyerCount++;
|
||||
|
||||
uint256 const buyOffer1 = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID1, drops(1)), token::owner(minter));
|
||||
buyerCount++;
|
||||
|
||||
env.close();
|
||||
BEAST_EXPECT(lastClose(env) < expiration);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 3);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// An unexpired offer can be brokered.
|
||||
env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
|
||||
minterCount--;
|
||||
|
||||
// Close enough ledgers to get past the expiration.
|
||||
while (lastClose(env) < expiration)
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
// If the sell offer is expired it cannot be brokered.
|
||||
env(token::brokerOffers(issuer, buyOffer1, sellOffer1), ter(tecEXPIRED));
|
||||
env.close();
|
||||
|
||||
// The expired sell offer is still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// With amendment: expired offers are deleted
|
||||
minterCount--;
|
||||
}
|
||||
|
||||
// Anyone can cancel the expired sell offer.
|
||||
env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// The buy offer was deleted, so no need to cancel it
|
||||
// The sell offer still exists, so we can cancel it
|
||||
env(token::cancelOffer(buyer, {buyOffer1}));
|
||||
buyerCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Anyone can cancel the expired offers
|
||||
env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
|
||||
minterCount--;
|
||||
buyerCount--;
|
||||
}
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
// Ensure that owner counts are the same with and without the
|
||||
// amendment
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0 && issuerCount == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1 && minterCount == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1 && buyerCount == 1);
|
||||
|
||||
// Transfer nftokenID0 back to minter so we start the next test in
|
||||
// a simple place.
|
||||
@@ -3014,9 +3141,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(token::acceptSellOffer(minter, offerSellBack));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
}
|
||||
// Show that in brokered mode:
|
||||
// 1. An unexpired buy offer with an expiration can be accepted.
|
||||
@@ -3054,17 +3182,28 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// If the buy offer is expired it cannot be brokered.
|
||||
env(token::brokerOffers(issuer, buyOffer1, sellOffer1), ter(tecEXPIRED));
|
||||
env.close();
|
||||
|
||||
// The expired buy offer is still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Anyone can cancel the expired buy offer.
|
||||
env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// After amendment: expired offers were deleted during broker
|
||||
// attempt
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
// The buy offer was deleted, so no need to cancel it
|
||||
// The sell offer still exists, so we can cancel it
|
||||
env(token::cancelOffer(minter, {sellOffer1}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Before amendment: expired offers still exist in ledger
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
// Anyone can cancel the expired offers
|
||||
env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
|
||||
}
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
@@ -3122,17 +3261,19 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// If the offers are expired they cannot be brokered.
|
||||
env(token::brokerOffers(issuer, buyOffer1, sellOffer1), ter(tecEXPIRED));
|
||||
env.close();
|
||||
|
||||
// The expired offers are still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Anyone can cancel the expired offers.
|
||||
env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
|
||||
if (!features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// Before amendment: expired offers still exist in ledger
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
// Anyone can cancel the expired offers
|
||||
env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
|
||||
}
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
@@ -6736,7 +6877,9 @@ public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testWithFeats(allFeatures - fixNFTokenReserve - featureNFTokenMintOffer - featureDynamicNFT);
|
||||
testWithFeats(
|
||||
allFeatures - fixNFTokenReserve - featureNFTokenMintOffer - featureDynamicNFT -
|
||||
fixExpiredNFTokenOfferRemoval);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6767,6 +6910,15 @@ class NFTokenWOModify_test : public NFTokenBaseUtil_test
|
||||
}
|
||||
};
|
||||
|
||||
class NFTokenWOExpiredOfferRemoval_test : public NFTokenBaseUtil_test
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testWithFeats(allFeatures - fixExpiredNFTokenOfferRemoval);
|
||||
}
|
||||
};
|
||||
|
||||
class NFTokenAllFeatures_test : public NFTokenBaseUtil_test
|
||||
{
|
||||
void
|
||||
|
||||
1264
src/test/app/TestHostFunctions.h
Normal file
1264
src/test/app/TestHostFunctions.h
Normal file
File diff suppressed because it is too large
Load Diff
967
src/test/app/Wasm_test.cpp
Normal file
967
src/test/app/Wasm_test.cpp
Normal file
@@ -0,0 +1,967 @@
|
||||
#ifdef _DEBUG
|
||||
// #define DEBUG_OUTPUT 1
|
||||
#endif
|
||||
|
||||
#include <test/app/TestHostFunctions.h>
|
||||
|
||||
#include <xrpld/app/wasm/HostFuncWrapper.h>
|
||||
|
||||
#include <source_location>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
|
||||
bool
|
||||
testGetDataIncrement();
|
||||
|
||||
using Add_proto = int32_t(int32_t, int32_t);
|
||||
static wasm_trap_t*
|
||||
Add(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
|
||||
{
|
||||
int32_t Val1 = params->data[0].of.i32;
|
||||
int32_t Val2 = params->data[1].of.i32;
|
||||
// printf("Host function \"Add\": %d + %d\n", Val1, Val2);
|
||||
results->data[0] = WASM_I32_VAL(Val1 + Val2);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> const
|
||||
hexToBytes(std::string const& hex)
|
||||
{
|
||||
auto const ws = boost::algorithm::unhex(hex);
|
||||
return Bytes(ws.begin(), ws.end());
|
||||
}
|
||||
|
||||
std::optional<int32_t>
|
||||
runFinishFunction(std::string const& code)
|
||||
{
|
||||
auto& engine = WasmEngine::instance();
|
||||
auto const wasm = hexToBytes(code);
|
||||
auto const re = engine.run(wasm, "finish");
|
||||
if (re.has_value())
|
||||
{
|
||||
return std::optional<int32_t>(re->result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
struct Wasm_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
checkResult(
|
||||
Expected<WasmResult<int32_t>, TER> re,
|
||||
int32_t expectedResult,
|
||||
int64_t expectedCost,
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
auto const lineStr = " (" + std::to_string(location.line()) + ")";
|
||||
if (BEAST_EXPECTS(re.has_value(), transToken(re.error()) + lineStr))
|
||||
{
|
||||
BEAST_EXPECTS(re->result == expectedResult, std::to_string(re->result) + lineStr);
|
||||
BEAST_EXPECTS(re->cost == expectedCost, std::to_string(re->cost) + lineStr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testGetDataHelperFunctions()
|
||||
{
|
||||
testcase("getData helper functions");
|
||||
BEAST_EXPECT(testGetDataIncrement());
|
||||
}
|
||||
|
||||
void
|
||||
testWasmLib()
|
||||
{
|
||||
testcase("wasmtime lib test");
|
||||
// clang-format off
|
||||
/* The WASM module buffer. */
|
||||
Bytes const wasm = {/* WASM header */
|
||||
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00,
|
||||
/* Type section */
|
||||
0x01, 0x07, 0x01,
|
||||
/* function type {i32, i32} -> {i32} */
|
||||
0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F,
|
||||
/* Import section */
|
||||
0x02, 0x13, 0x01,
|
||||
/* module name: "extern" */
|
||||
0x06, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6E,
|
||||
/* extern name: "func-add" */
|
||||
0x08, 0x66, 0x75, 0x6E, 0x63, 0x2D, 0x61, 0x64, 0x64,
|
||||
/* import desc: func 0 */
|
||||
0x00, 0x00,
|
||||
/* Function section */
|
||||
0x03, 0x02, 0x01, 0x00,
|
||||
/* Export section */
|
||||
0x07, 0x0A, 0x01,
|
||||
/* export name: "addTwo" */
|
||||
0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6F,
|
||||
/* export desc: func 0 */
|
||||
0x00, 0x01,
|
||||
/* Code section */
|
||||
0x0A, 0x0A, 0x01,
|
||||
/* code body */
|
||||
0x08, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x00, 0x0B};
|
||||
// clang-format on
|
||||
auto& vm = WasmEngine::instance();
|
||||
|
||||
std::shared_ptr<ImportVec> imports(std::make_shared<ImportVec>());
|
||||
WasmImpFunc<Add_proto>(*imports, "func-add", reinterpret_cast<void*>(&Add));
|
||||
|
||||
auto re = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports);
|
||||
|
||||
// if (res) printf("invokeAdd get the result: %d\n", res.value());
|
||||
|
||||
checkResult(re, 6'912, 59);
|
||||
}
|
||||
|
||||
void
|
||||
testBadWasm()
|
||||
{
|
||||
testcase("bad wasm test");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
std::shared_ptr<HostFunctions> hfs(new HostFunctions(env.journal));
|
||||
|
||||
{
|
||||
auto wasm = hexToBytes("00000000");
|
||||
std::string funcName("mock_escrow");
|
||||
|
||||
auto re = runEscrowWasm(wasm, hfs, funcName, {}, 15);
|
||||
BEAST_EXPECT(!re);
|
||||
}
|
||||
|
||||
{
|
||||
auto wasm = hexToBytes("00112233445566778899AA");
|
||||
std::string funcName("mock_escrow");
|
||||
|
||||
auto const re = preflightEscrowWasm(wasm, hfs, funcName);
|
||||
BEAST_EXPECT(!isTesSuccess(re));
|
||||
}
|
||||
|
||||
{
|
||||
// FinishFunction wrong function name
|
||||
// pub fn bad() -> bool {
|
||||
// unsafe { host_lib::getLedgerSqn() >= 5 }
|
||||
// }
|
||||
auto const badWasm = hexToBytes(
|
||||
"0061736d010000000105016000017f02190108686f73745f6c69620c6765"
|
||||
"744c656467657253716e00000302010005030100100611027f00418080c0"
|
||||
"000b7f00418080c0000b072b04066d656d6f727902000362616400010a5f"
|
||||
"5f646174615f656e6403000b5f5f686561705f6261736503010a09010700"
|
||||
"100041044a0b004d0970726f64756365727302086c616e67756167650104"
|
||||
"52757374000c70726f6365737365642d6279010572757374631d312e3835"
|
||||
"2e31202834656231363132353020323032352d30332d31352900490f7461"
|
||||
"726765745f6665617475726573042b0f6d757461626c652d676c6f62616c"
|
||||
"732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a"
|
||||
"6d756c746976616c7565");
|
||||
|
||||
auto const re = preflightEscrowWasm(badWasm, hfs, ESCROW_FUNCTION_NAME);
|
||||
BEAST_EXPECT(!isTesSuccess(re));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testWasmLedgerSqn()
|
||||
{
|
||||
testcase("Wasm get ledger sequence");
|
||||
|
||||
auto ledgerSqnWasm = hexToBytes(ledgerSqnWasmHex);
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
std::shared_ptr<HostFunctions> hfs(new TestLedgerDataProvider(env));
|
||||
auto imports = std::make_shared<ImportVec>();
|
||||
WASM_IMPORT_FUNC2(*imports, getLedgerSqn, "get_ledger_sqn", hfs.get(), 33);
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto re = engine.run(ledgerSqnWasm, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal);
|
||||
|
||||
checkResult(re, 0, 440);
|
||||
|
||||
env.close();
|
||||
env.close();
|
||||
|
||||
// empty module - run the same instance
|
||||
re = engine.run({}, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal);
|
||||
|
||||
checkResult(re, 5, 488);
|
||||
}
|
||||
|
||||
void
|
||||
testWasmFib()
|
||||
{
|
||||
testcase("Wasm fibo");
|
||||
|
||||
auto const fibWasm = hexToBytes(fibWasmHex);
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto const re = engine.run(fibWasm, "fib", wasmParams(10));
|
||||
|
||||
checkResult(re, 55, 1'137);
|
||||
}
|
||||
|
||||
void
|
||||
testWasmSha()
|
||||
{
|
||||
testcase("Wasm sha");
|
||||
|
||||
auto const sha512Wasm = hexToBytes(sha512PureWasmHex);
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto const re = engine.run(sha512Wasm, "sha512_process", wasmParams(sha512PureWasmHex));
|
||||
|
||||
checkResult(re, 34'432, 151'155);
|
||||
}
|
||||
|
||||
void
|
||||
testWasmB58()
|
||||
{
|
||||
testcase("Wasm base58");
|
||||
auto const b58Wasm = hexToBytes(b58WasmHex);
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
Bytes outb;
|
||||
outb.resize(1024);
|
||||
|
||||
auto const minsz = std::min(static_cast<std::uint32_t>(512), static_cast<std::uint32_t>(b58WasmHex.size()));
|
||||
auto const s = std::string_view(b58WasmHex.c_str(), minsz);
|
||||
|
||||
auto const re = engine.run(b58Wasm, "b58enco", wasmParams(outb, s));
|
||||
|
||||
checkResult(re, 700, 2'886'069);
|
||||
}
|
||||
|
||||
void
|
||||
testHFCost()
|
||||
{
|
||||
testcase("wasm test host functions cost");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env(*this);
|
||||
{
|
||||
auto const allHostFuncWasm = hexToBytes(allHostFunctionsWasmHex);
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
auto imp = createWasmImport(*hfs);
|
||||
for (auto& i : *imp)
|
||||
i.second.gas = 0;
|
||||
|
||||
auto re = engine.run(allHostFuncWasm, ESCROW_FUNCTION_NAME, {}, imp, hfs, 1'000'000, env.journal);
|
||||
|
||||
checkResult(re, 1, 27'080);
|
||||
|
||||
env.close();
|
||||
}
|
||||
|
||||
env.close();
|
||||
env.close();
|
||||
env.close();
|
||||
env.close();
|
||||
env.close();
|
||||
|
||||
{
|
||||
auto const allHostFuncWasm = hexToBytes(allHostFunctionsWasmHex);
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
auto const imp = createWasmImport(*hfs);
|
||||
|
||||
auto re = engine.run(allHostFuncWasm, ESCROW_FUNCTION_NAME, {}, imp, hfs, 1'000'000, env.journal);
|
||||
|
||||
checkResult(re, 1, 66'340);
|
||||
|
||||
env.close();
|
||||
}
|
||||
|
||||
// not enough gas
|
||||
{
|
||||
auto const allHostFuncWasm = hexToBytes(allHostFunctionsWasmHex);
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
auto const imp = createWasmImport(*hfs);
|
||||
|
||||
auto re = engine.run(allHostFuncWasm, ESCROW_FUNCTION_NAME, {}, imp, hfs, 200, env.journal);
|
||||
|
||||
if (BEAST_EXPECT(!re))
|
||||
{
|
||||
BEAST_EXPECTS(re.error() == tecFAILED_PROCESSING, std::to_string(TERtoInt(re.error())));
|
||||
}
|
||||
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testEscrowWasmDN()
|
||||
{
|
||||
testcase("escrow wasm devnet test");
|
||||
|
||||
auto const allHFWasm = hexToBytes(allHostFunctionsWasmHex);
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
{
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
auto re = runEscrowWasm(allHFWasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000);
|
||||
checkResult(re, 1, 66'340);
|
||||
}
|
||||
|
||||
{
|
||||
// max<int64_t>() gas
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
auto re = runEscrowWasm(allHFWasm, hfs, ESCROW_FUNCTION_NAME, {}, -1);
|
||||
checkResult(re, 1, 66'340);
|
||||
}
|
||||
|
||||
{ // fail because trying to access nonexistent field
|
||||
struct BadTestHostFunctions : public TestHostFunctions
|
||||
{
|
||||
explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env)
|
||||
{
|
||||
}
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
return Unexpected(HostFunctionError::FIELD_NOT_FOUND);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<HostFunctions> hfs(new BadTestHostFunctions(env));
|
||||
auto re = runEscrowWasm(allHFWasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000);
|
||||
checkResult(re, -201, 28'965);
|
||||
}
|
||||
|
||||
{ // fail because trying to allocate more than MAX_PAGES memory
|
||||
struct BadTestHostFunctions : public TestHostFunctions
|
||||
{
|
||||
explicit BadTestHostFunctions(Env& env) : TestHostFunctions(env)
|
||||
{
|
||||
}
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) override
|
||||
{
|
||||
return Bytes((128 + 1) * 64 * 1024, 1);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<HostFunctions> hfs(new BadTestHostFunctions(env));
|
||||
auto re = runEscrowWasm(allHFWasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000);
|
||||
checkResult(re, -201, 28'965);
|
||||
}
|
||||
|
||||
{ // fail because recursion too deep
|
||||
|
||||
auto const deepWasm = hexToBytes(deepRecursionHex);
|
||||
|
||||
std::shared_ptr<TestHostFunctionsSink> hfs(new TestHostFunctionsSink(env));
|
||||
std::string funcName("finish");
|
||||
auto re = runEscrowWasm(deepWasm, hfs, funcName, {}, 1'000'000'000);
|
||||
BEAST_EXPECT(!re && re.error());
|
||||
// std::cout << "bad case (deep recursion) result " << re.error()
|
||||
// << std::endl;
|
||||
|
||||
auto const& sink = hfs->getSink();
|
||||
auto countSubstr = [](std::string const& str, std::string const& substr) {
|
||||
std::size_t pos = 0;
|
||||
int occurrences = 0;
|
||||
while ((pos = str.find(substr, pos)) != std::string::npos)
|
||||
{
|
||||
occurrences++;
|
||||
pos += substr.length();
|
||||
}
|
||||
return occurrences;
|
||||
};
|
||||
|
||||
auto const s = sink.messages().str();
|
||||
BEAST_EXPECT(countSubstr(s, "WASMI Error: failure to call func") == 1);
|
||||
BEAST_EXPECT(countSubstr(s, "exception: <finish> failure") > 0);
|
||||
}
|
||||
|
||||
{ // infinite loop
|
||||
auto const infiniteLoopWasm = hexToBytes(infiniteLoopWasmHex);
|
||||
std::string const funcName("loop");
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
|
||||
// infinite loop should be caught and fail
|
||||
auto const re = runEscrowWasm(infiniteLoopWasm, hfs, funcName, {}, 1'000'000);
|
||||
if (BEAST_EXPECT(!re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// expected import not provided
|
||||
auto const lgrSqnWasm = hexToBytes(ledgerSqnWasmHex);
|
||||
std::shared_ptr<HostFunctions> hfs(new TestLedgerDataProvider(env));
|
||||
std::shared_ptr<ImportVec> imports(std::make_shared<ImportVec>());
|
||||
WASM_IMPORT_FUNC2(*imports, getLedgerSqn, "get_ledger_sqn2", hfs.get());
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto re = engine.run(lgrSqnWasm, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal);
|
||||
|
||||
BEAST_EXPECT(!re);
|
||||
}
|
||||
|
||||
{
|
||||
// bad import format
|
||||
auto const lgrSqnWasm = hexToBytes(ledgerSqnWasmHex);
|
||||
std::shared_ptr<HostFunctions> hfs(new TestLedgerDataProvider(env));
|
||||
std::shared_ptr<ImportVec> imports(std::make_shared<ImportVec>());
|
||||
WASM_IMPORT_FUNC2(*imports, getLedgerSqn, "get_ledger_sqn", hfs.get());
|
||||
(*imports)[0].first = nullptr;
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto re = engine.run(lgrSqnWasm, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal);
|
||||
|
||||
BEAST_EXPECT(!re);
|
||||
}
|
||||
|
||||
{
|
||||
// bad function name
|
||||
auto const lgrSqnWasm = hexToBytes(ledgerSqnWasmHex);
|
||||
std::shared_ptr<HostFunctions> hfs(new TestLedgerDataProvider(env));
|
||||
std::shared_ptr<ImportVec> imports(std::make_shared<ImportVec>());
|
||||
WASM_IMPORT_FUNC2(*imports, getLedgerSqn, "get_ledger_sqn", hfs.get());
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
auto re = engine.run(lgrSqnWasm, "func1", {}, imports, hfs, 1'000'000, env.journal);
|
||||
|
||||
BEAST_EXPECT(!re);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testFloat()
|
||||
{
|
||||
testcase("float point");
|
||||
|
||||
std::string const funcName("finish");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env(*this);
|
||||
{
|
||||
auto const floatTestWasm = hexToBytes(floatTestsWasmHex);
|
||||
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
auto re = runEscrowWasm(floatTestWasm, hfs, funcName, {}, 200'000);
|
||||
checkResult(re, 1, 110'699);
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
auto const float0Wasm = hexToBytes(float0Hex);
|
||||
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
auto re = runEscrowWasm(float0Wasm, hfs, funcName, {}, 100'000);
|
||||
checkResult(re, 1, 4'259);
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
perfTest()
|
||||
{
|
||||
testcase("Perf test host functions");
|
||||
|
||||
using namespace jtx;
|
||||
using namespace std::chrono;
|
||||
|
||||
// std::string const funcName("test");
|
||||
auto const perfWasm = hexToBytes(hfPerfTest);
|
||||
|
||||
// std::string const credType = "abcde";
|
||||
// std::string const credType2 = "fghijk";
|
||||
// std::string const credType3 = "0123456";
|
||||
// char const uri[] = "uri";
|
||||
|
||||
Account const alan{"alan"};
|
||||
Account const bob{"bob"};
|
||||
Account const issuer{"issuer"};
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
// Env env(*this, envconfig(), {}, nullptr,
|
||||
// beast::severities::kTrace);
|
||||
env.fund(XRP(5000), alan, bob, issuer);
|
||||
env.close();
|
||||
|
||||
// // create escrow
|
||||
// auto const seq = env.seq(alan);
|
||||
// auto const k = keylet::escrow(alan, seq);
|
||||
// // auto const allowance = 3'600;
|
||||
// auto escrowCreate = escrow::create(alan, bob, XRP(1000));
|
||||
// XRPAmount txnFees = env.current()->fees().base + 1000;
|
||||
// env(escrowCreate,
|
||||
// escrow::finish_function(wasmHex),
|
||||
// escrow::finish_time(env.now() + 11s),
|
||||
// escrow::cancel_time(env.now() + 100s),
|
||||
// escrow::data("1000000000"), // 1000 XRP in drops
|
||||
// memodata("memo1234567"),
|
||||
// memodata("2memo1234567"),
|
||||
// fee(txnFees));
|
||||
|
||||
// // create depositPreauth
|
||||
// auto const k = keylet::depositPreauth(
|
||||
// bob,
|
||||
// {{issuer.id(), makeSlice(credType)},
|
||||
// {issuer.id(), makeSlice(credType2)},
|
||||
// {issuer.id(), makeSlice(credType3)}});
|
||||
// env(deposit::authCredentials(
|
||||
// bob,
|
||||
// {{issuer, credType},
|
||||
// {issuer, credType2},
|
||||
// {issuer, credType3}}));
|
||||
|
||||
// create nft
|
||||
[[maybe_unused]] uint256 const nft0{token::getNextID(env, alan, 0u)};
|
||||
env(token::mint(alan, 0u));
|
||||
auto const k = keylet::nftoffer(alan, 0);
|
||||
[[maybe_unused]] uint256 const nft1{token::getNextID(env, alan, 0u)};
|
||||
|
||||
env(token::mint(alan, 0u),
|
||||
token::uri("https://github.com/XRPLF/XRPL-Standards/discussions/"
|
||||
"279?id=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||
"279&ut=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||
"279&sid=github.com/XRPLF/XRPL-Standards/discussions/"
|
||||
"279&aot=github.com/XRPLF/XRPL-Standards/disc"));
|
||||
[[maybe_unused]] uint256 const nft2{token::getNextID(env, alan, 0u)};
|
||||
env(token::mint(alan, 0u));
|
||||
env.close();
|
||||
|
||||
std::shared_ptr<HostFunctions> hfs(new PerfHostFunctions(env, k, env.tx()));
|
||||
|
||||
auto re = runEscrowWasm(perfWasm, hfs, ESCROW_FUNCTION_NAME);
|
||||
if (BEAST_EXPECT(re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(re->result);
|
||||
std::cout << "Res: " << re->result << " cost: " << re->cost << std::endl;
|
||||
}
|
||||
|
||||
// env(escrow::finish(alan, alan, seq),
|
||||
// escrow::comp_allowance(allowance),
|
||||
// fee(txnFees),
|
||||
// ter(tesSUCCESS));
|
||||
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCodecovWasm()
|
||||
{
|
||||
testcase("Codecov wasm test");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
|
||||
auto const codecovWasm = hexToBytes(codecovTestsWasmHex);
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
|
||||
auto const allowance = 201'503;
|
||||
auto re = runEscrowWasm(codecovWasm, hfs, ESCROW_FUNCTION_NAME, {}, allowance);
|
||||
|
||||
checkResult(re, 1, allowance);
|
||||
}
|
||||
|
||||
void
|
||||
testDisabledFloat()
|
||||
{
|
||||
testcase("disabled float");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
|
||||
auto disabledFloatWasm = hexToBytes(disabledFloatHex);
|
||||
std::string const funcName("finish");
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
|
||||
{
|
||||
// f32 set constant, opcode disabled exception
|
||||
auto const re = runEscrowWasm(disabledFloatWasm, hfs, funcName, {}, 1'000'000);
|
||||
if (BEAST_EXPECT(!re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// f32 add, can't create module exception
|
||||
disabledFloatWasm[0x117] = 0x92;
|
||||
auto const re = runEscrowWasm(disabledFloatWasm, hfs, funcName, {}, 1'000'000);
|
||||
if (BEAST_EXPECT(!re.has_value()))
|
||||
{
|
||||
BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testWasmMemory()
|
||||
{
|
||||
testcase("Wasm additional memory limit tests");
|
||||
BEAST_EXPECT(runFinishFunction(memoryPointerAtLimitHex).value() == 1);
|
||||
BEAST_EXPECT(runFinishFunction(memoryPointerOverLimitHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(memoryOffsetOverLimitHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(memoryEndOfWordOverLimitHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(memoryGrow0To1PageHex).value() == 1);
|
||||
BEAST_EXPECT(runFinishFunction(memoryGrow1To0PageHex).value() == -1);
|
||||
BEAST_EXPECT(runFinishFunction(memoryLastByteOf8MBHex).value() == 1);
|
||||
BEAST_EXPECT(runFinishFunction(memoryGrow1MoreThan8MBHex).value() == -1);
|
||||
BEAST_EXPECT(runFinishFunction(memoryGrow0MoreThan8MBHex).value() == 1);
|
||||
BEAST_EXPECT(runFinishFunction(memoryInit1MoreThan8MBHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(memoryNegativeAddressHex).has_value() == false);
|
||||
}
|
||||
|
||||
void
|
||||
testWasmTable()
|
||||
{
|
||||
testcase("Wasm table limit tests");
|
||||
BEAST_EXPECT(runFinishFunction(table64ElementsHex).value() == 1);
|
||||
BEAST_EXPECT(runFinishFunction(table65ElementsHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(table2TablesHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(table0ElementsHex).value() == 1);
|
||||
BEAST_EXPECT(runFinishFunction(tableUintMaxHex).has_value() == false);
|
||||
}
|
||||
|
||||
void
|
||||
testWasmProposal()
|
||||
{
|
||||
testcase("Wasm disabled proposal tests");
|
||||
BEAST_EXPECT(runFinishFunction(proposalMutableGlobalHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalGcStructNewHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalMultiValueHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalSignExtHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalFloatToIntHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalBulkMemoryHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalRefTypesHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalTailCallHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalExtendedConstHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalMultiMemoryHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalCustomPageSizesHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalMemory64Hex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(proposalWideArithmeticHex).has_value() == false);
|
||||
}
|
||||
|
||||
void
|
||||
testWasmTrap()
|
||||
{
|
||||
testcase("Wasm trap tests");
|
||||
BEAST_EXPECT(runFinishFunction(trapDivideBy0Hex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(trapIntOverflowHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(trapUnreachableHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(trapNullCallHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(trapFuncSigMismatchHex).has_value() == false);
|
||||
}
|
||||
|
||||
void
|
||||
testWasmWasi()
|
||||
{
|
||||
testcase("Wasm Wasi tests");
|
||||
BEAST_EXPECT(runFinishFunction(wasiGetTimeHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(wasiPrintHex).has_value() == false);
|
||||
}
|
||||
|
||||
void
|
||||
testWasmSectionCorruption()
|
||||
{
|
||||
testcase("Wasm Section Corruption tests");
|
||||
BEAST_EXPECT(runFinishFunction(badMagicNumberHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(badVersionNumberHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(lyingHeaderHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(neverEndingNumberHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(vectorLieHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(sectionOrderingHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(ghostPayloadHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(junkAfterSectionHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(invalidSectionIdHex).has_value() == false);
|
||||
BEAST_EXPECT(runFinishFunction(localVariableBombHex).has_value() == false);
|
||||
}
|
||||
|
||||
void
|
||||
testStartFunctionLoop()
|
||||
{
|
||||
testcase("infinite loop in start function");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
|
||||
auto const startLoopWasm = hexToBytes(startLoopHex);
|
||||
std::shared_ptr<HostFunctions> hfs(new TestLedgerDataProvider(env));
|
||||
std::shared_ptr<ImportVec> imports(std::make_shared<ImportVec>());
|
||||
|
||||
auto& engine = WasmEngine::instance();
|
||||
auto checkRes = engine.check(startLoopWasm, "finish", {}, imports, hfs, env.journal);
|
||||
BEAST_EXPECTS(checkRes == tesSUCCESS, std::to_string(TERtoInt(checkRes)));
|
||||
|
||||
auto re = engine.run(startLoopWasm, ESCROW_FUNCTION_NAME, {}, imports, hfs, 1'000'000, env.journal);
|
||||
BEAST_EXPECTS(re.error() == tecFAILED_PROCESSING, std::to_string(TERtoInt(re.error())));
|
||||
}
|
||||
|
||||
void
|
||||
testBadAlloc()
|
||||
{
|
||||
testcase("Wasm Bad Alloc");
|
||||
|
||||
// bad_alloc.c
|
||||
auto const badAllocWasm = hexToBytes(badAllocHex);
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
std::shared_ptr<HostFunctions> hfs(new TestLedgerDataProvider(env));
|
||||
|
||||
// std::shared_ptr<ImportVec> imports(std::make_shared<ImportVec>());
|
||||
uint8_t buf1[8] = {7, 8, 9, 10, 11, 12, 13, 14};
|
||||
{ // forged "allocate" return valid address
|
||||
std::vector<WasmParam> params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = sizeof(buf1)}}}};
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal);
|
||||
if (BEAST_EXPECT(re))
|
||||
{
|
||||
BEAST_EXPECTS(re->result == 7, std::to_string(re->result));
|
||||
BEAST_EXPECTS(re->cost == 430, std::to_string(re->cost));
|
||||
}
|
||||
}
|
||||
|
||||
{ // return 0 whithout calling wasm
|
||||
std::vector<WasmParam> params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = 0}}}};
|
||||
auto& engine = WasmEngine::instance();
|
||||
auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal);
|
||||
BEAST_EXPECT(!re) && BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||
}
|
||||
|
||||
{ // forged "allocate" return 8Mb (which is more than memory limit)
|
||||
std::vector<WasmParam> params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = 1}}}};
|
||||
auto& engine = WasmEngine::instance();
|
||||
auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal);
|
||||
BEAST_EXPECT(!re) && BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||
}
|
||||
|
||||
{ // forged "allocate" return 0
|
||||
std::vector<WasmParam> params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = 2}}}};
|
||||
auto& engine = WasmEngine::instance();
|
||||
auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal);
|
||||
BEAST_EXPECT(!re) && BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||
}
|
||||
|
||||
{ // forged "allocate" return -1
|
||||
std::vector<WasmParam> params = {{.type = WT_U8V, .of = {.u8v = {.d = buf1, .sz = 3}}}};
|
||||
auto& engine = WasmEngine::instance();
|
||||
auto re = engine.run(badAllocWasm, "test", params, {}, hfs, 1'000'000, env.journal);
|
||||
|
||||
BEAST_EXPECT(!re) && BEAST_EXPECT(re.error() == tecFAILED_PROCESSING);
|
||||
}
|
||||
|
||||
{
|
||||
std::string what;
|
||||
try
|
||||
{
|
||||
char const test[] = "test";
|
||||
std::size_t sz = std::numeric_limits<int32_t>::max() + 1ull;
|
||||
auto p = wasmParams(std::string_view(test, sz));
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
what = e.what();
|
||||
}
|
||||
BEAST_EXPECT(what.find("can't allocate memory, size: 2147483648") != std::string::npos);
|
||||
}
|
||||
|
||||
env.close();
|
||||
}
|
||||
|
||||
void
|
||||
testBadAlign()
|
||||
{
|
||||
testcase("Wasm Bad Align");
|
||||
|
||||
// bad_align.c
|
||||
auto const badAlignWasm = hexToBytes(badAlignWasmHex);
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
auto imports = createWasmImport(*hfs);
|
||||
|
||||
{ // Calls float_from_uint with bad aligment.
|
||||
// Can be checked through codecov
|
||||
auto& engine = WasmEngine::instance();
|
||||
|
||||
auto re = engine.run(badAlignWasm, "test", {}, imports, hfs, 1'000'000, env.journal);
|
||||
if (BEAST_EXPECTS(re, transToken(re.error())))
|
||||
{
|
||||
BEAST_EXPECTS(re->result == 0x684f7941, std::to_string(re->result));
|
||||
}
|
||||
}
|
||||
|
||||
env.close();
|
||||
}
|
||||
|
||||
void
|
||||
testReturnType()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
std::shared_ptr<HostFunctions> hfs(new TestHostFunctions(env, 0));
|
||||
|
||||
// return int64.
|
||||
{ // (module
|
||||
// (memory (export "memory") 1)
|
||||
// (func (export "finish") (result i64)
|
||||
// i64.const 0x100000000))
|
||||
auto const wasmHex =
|
||||
"0061736d010000000105016000017e030201000503010001"
|
||||
"071302066d656d6f727902000666696e69736800000a0a01"
|
||||
"08004280808080100b";
|
||||
auto const wasm = hexToBytes(wasmHex);
|
||||
auto const re = runEscrowWasm(wasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000);
|
||||
BEAST_EXPECT(!re);
|
||||
}
|
||||
|
||||
// return void. wasmi return execution error
|
||||
{ //(module
|
||||
// (type (;0;) (func))
|
||||
// (func (;0;) (type 0)
|
||||
// return)
|
||||
// (memory (;0;) 1)
|
||||
// (export "memory" (memory 0))
|
||||
// (export "finish" (func 0)))
|
||||
auto const wasmHex =
|
||||
"0061736d01000000010401600000030201000503010001071302066d656d6f"
|
||||
"727902000666696e69736800000a050103000f0b";
|
||||
auto const wasm = hexToBytes(wasmHex);
|
||||
auto const re = runEscrowWasm(wasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000);
|
||||
BEAST_EXPECT(!re);
|
||||
}
|
||||
|
||||
// return i32, i32. wasmi doesn't create module
|
||||
{ //(module
|
||||
// (memory (export "memory") 1)
|
||||
// (func (export "finish") (result i32 i32)
|
||||
// i32.const 0x10000000
|
||||
// i32.const 0x100000FF))
|
||||
auto const wasmHex =
|
||||
"0061736d010000000106016000027f7f030201000503010001071302066d65"
|
||||
"6d6f727902000666696e69736800000a10010e0041808080800141ff818080"
|
||||
"010b";
|
||||
auto const wasm = hexToBytes(wasmHex);
|
||||
auto const re = runEscrowWasm(wasm, hfs, ESCROW_FUNCTION_NAME, {}, 100'000);
|
||||
BEAST_EXPECT(!re);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testSwapBytes()
|
||||
{
|
||||
testcase("Wasm swap bytes");
|
||||
|
||||
uint64_t const SWAP_DATAU64 = 0x123456789abcdeffull;
|
||||
uint64_t const REVERSE_SWAP_DATAU64 = 0xffdebc9a78563412ull;
|
||||
int64_t const SWAP_DATAI64 = 0x123456789abcdeffll;
|
||||
int64_t const REVERSE_SWAP_DATAI64 = 0xffdebc9a78563412ll;
|
||||
|
||||
uint32_t const SWAP_DATAU32 = 0x12789aff;
|
||||
uint32_t const REVERSE_SWAP_DATAU32 = 0xff9a7812;
|
||||
int32_t const SWAP_DATAI32 = 0x12789aff;
|
||||
int32_t const REVERSE_SWAP_DATAI32 = 0xff9a7812;
|
||||
|
||||
uint16_t const SWAP_DATAU16 = 0x12ff;
|
||||
uint16_t const REVERSE_SWAP_DATAU16 = 0xff12;
|
||||
int16_t const SWAP_DATAI16 = 0x12ff;
|
||||
int16_t const REVERSE_SWAP_DATAI16 = 0xff12;
|
||||
|
||||
uint64_t b1 = SWAP_DATAU64;
|
||||
int64_t b2 = SWAP_DATAI64;
|
||||
b1 = adjustWasmEndianessHlp(b1);
|
||||
b2 = adjustWasmEndianessHlp(b2);
|
||||
BEAST_EXPECT(b1 == REVERSE_SWAP_DATAU64);
|
||||
BEAST_EXPECT(b2 == REVERSE_SWAP_DATAI64);
|
||||
b1 = adjustWasmEndianessHlp(b1);
|
||||
b2 = adjustWasmEndianessHlp(b2);
|
||||
BEAST_EXPECT(b1 == SWAP_DATAU64);
|
||||
BEAST_EXPECT(b2 == SWAP_DATAI64);
|
||||
|
||||
uint32_t b3 = SWAP_DATAU32;
|
||||
int32_t b4 = SWAP_DATAI32;
|
||||
b3 = adjustWasmEndianessHlp(b3);
|
||||
b4 = adjustWasmEndianessHlp(b4);
|
||||
BEAST_EXPECT(b3 == REVERSE_SWAP_DATAU32);
|
||||
BEAST_EXPECT(b4 == REVERSE_SWAP_DATAI32);
|
||||
b3 = adjustWasmEndianessHlp(b3);
|
||||
b4 = adjustWasmEndianessHlp(b4);
|
||||
BEAST_EXPECT(b3 == SWAP_DATAU32);
|
||||
BEAST_EXPECT(b4 == SWAP_DATAI32);
|
||||
|
||||
uint16_t b5 = SWAP_DATAU16;
|
||||
int16_t b6 = SWAP_DATAI16;
|
||||
b5 = adjustWasmEndianessHlp(b5);
|
||||
b6 = adjustWasmEndianessHlp(b6);
|
||||
BEAST_EXPECT(b5 == REVERSE_SWAP_DATAU16);
|
||||
BEAST_EXPECT(b6 == REVERSE_SWAP_DATAI16);
|
||||
b5 = adjustWasmEndianessHlp(b5);
|
||||
b6 = adjustWasmEndianessHlp(b6);
|
||||
BEAST_EXPECT(b5 == SWAP_DATAU16);
|
||||
BEAST_EXPECT(b6 == SWAP_DATAI16);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
testGetDataHelperFunctions();
|
||||
testWasmLib();
|
||||
testBadWasm();
|
||||
testWasmLedgerSqn();
|
||||
|
||||
testWasmFib();
|
||||
testWasmSha();
|
||||
testWasmB58();
|
||||
|
||||
testHFCost();
|
||||
testEscrowWasmDN();
|
||||
testFloat();
|
||||
|
||||
testCodecovWasm();
|
||||
testDisabledFloat();
|
||||
|
||||
testWasmMemory();
|
||||
testWasmTable();
|
||||
testWasmProposal();
|
||||
testWasmTrap();
|
||||
testWasmWasi();
|
||||
testWasmSectionCorruption();
|
||||
|
||||
testStartFunctionLoop();
|
||||
testBadAlloc();
|
||||
testBadAlign();
|
||||
testReturnType();
|
||||
testSwapBytes();
|
||||
|
||||
// perfTest();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Wasm, app, xrpl);
|
||||
|
||||
} // namespace test
|
||||
} // namespace xrpl
|
||||
3
src/test/app/wasm_fixtures/.gitignore
vendored
Normal file
3
src/test/app/wasm_fixtures/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
**/target
|
||||
**/debug
|
||||
*.wasm
|
||||
171
src/test/app/wasm_fixtures/all_host_functions/Cargo.lock
generated
Normal file
171
src/test/app/wasm_fixtures/all_host_functions/Cargo.lock
generated
Normal file
@@ -0,0 +1,171 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "all_host_functions"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"xrpl-wasm-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "xrpl-address-macro"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=u32-buffer#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"quote",
|
||||
"sha2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xrpl-wasm-stdlib"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=u32-buffer#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8"
|
||||
dependencies = [
|
||||
"xrpl-address-macro",
|
||||
]
|
||||
21
src/test/app/wasm_fixtures/all_host_functions/Cargo.toml
Normal file
21
src/test/app/wasm_fixtures/all_host_functions/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "all_host_functions"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
# This empty workspace definition keeps this project independent of the parent workspace
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
xrpl-std = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-wasm-stdlib", branch = "u32-buffer" }
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
783
src/test/app/wasm_fixtures/all_host_functions/src/lib.rs
Normal file
783
src/test/app/wasm_fixtures/all_host_functions/src/lib.rs
Normal file
@@ -0,0 +1,783 @@
|
||||
#![cfg_attr(target_arch = "wasm32", no_std)]
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
extern crate std;
|
||||
|
||||
//
|
||||
// Host Functions Test
|
||||
// Tests 26 host functions (across 7 categories)
|
||||
//
|
||||
// With craft you can run this test with:
|
||||
// craft test --project host_functions_test --test-case host_functions_test
|
||||
//
|
||||
// Amount Format Update:
|
||||
// - XRP amounts now return as 8-byte serialized rippled objects
|
||||
// - IOU and MPT amounts return in variable-length serialized format
|
||||
// - Format details: https://xrpl.org/docs/references/protocol/binary-format#amount-fields
|
||||
//
|
||||
// Error Code Ranges:
|
||||
// -100 to -199: Ledger Header Functions (3 functions)
|
||||
// -200 to -299: Transaction Data Functions (5 functions)
|
||||
// -300 to -399: Current Ledger Object Functions (4 functions)
|
||||
// -400 to -499: Any Ledger Object Functions (5 functions)
|
||||
// -500 to -599: Keylet Generation Functions (4 functions)
|
||||
// -600 to -699: Utility Functions (4 functions)
|
||||
// -700 to -799: Data Update Functions (1 function)
|
||||
//
|
||||
|
||||
use xrpl_std::core::current_tx::escrow_finish::EscrowFinish;
|
||||
use xrpl_std::core::current_tx::traits::TransactionCommonFields;
|
||||
use xrpl_std::host;
|
||||
use xrpl_std::host::trace::{trace, trace_account_buf, trace_data, trace_num, DataRepr};
|
||||
use xrpl_std::sfield;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn finish() -> i32 {
|
||||
let _ = trace("=== HOST FUNCTIONS TEST ===");
|
||||
let _ = trace("Testing 26 host functions");
|
||||
|
||||
// Category 1: Ledger Header Data Functions (3 functions)
|
||||
// Error range: -100 to -199
|
||||
match test_ledger_header_functions() {
|
||||
0 => (),
|
||||
err => return err,
|
||||
}
|
||||
|
||||
// Category 2: Transaction Data Functions (5 functions)
|
||||
// Error range: -200 to -299
|
||||
match test_transaction_data_functions() {
|
||||
0 => (),
|
||||
err => return err,
|
||||
}
|
||||
|
||||
// Category 3: Current Ledger Object Functions (4 functions)
|
||||
// Error range: -300 to -399
|
||||
match test_current_ledger_object_functions() {
|
||||
0 => (),
|
||||
err => return err,
|
||||
}
|
||||
|
||||
// Category 4: Any Ledger Object Functions (5 functions)
|
||||
// Error range: -400 to -499
|
||||
match test_any_ledger_object_functions() {
|
||||
0 => (),
|
||||
err => return err,
|
||||
}
|
||||
|
||||
// Category 5: Keylet Generation Functions (4 functions)
|
||||
// Error range: -500 to -599
|
||||
match test_keylet_generation_functions() {
|
||||
0 => (),
|
||||
err => return err,
|
||||
}
|
||||
|
||||
// Category 6: Utility Functions (4 functions)
|
||||
// Error range: -600 to -699
|
||||
match test_utility_functions() {
|
||||
0 => (),
|
||||
err => return err,
|
||||
}
|
||||
|
||||
// Category 7: Data Update Functions (1 function)
|
||||
// Error range: -700 to -799
|
||||
match test_data_update_functions() {
|
||||
0 => (),
|
||||
err => return err,
|
||||
}
|
||||
|
||||
let _ = trace("SUCCESS: All host function tests passed!");
|
||||
1 // Success return code for WASM finish function
|
||||
}
|
||||
|
||||
/// Test Category 1: Ledger Header Data Functions (3 functions)
|
||||
/// - get_ledger_sqn() - Get ledger sequence number
|
||||
/// - get_parent_ledger_time() - Get parent ledger timestamp
|
||||
/// - get_parent_ledger_hash() - Get parent ledger hash
|
||||
fn test_ledger_header_functions() -> i32 {
|
||||
let _ = trace("--- Category 1: Ledger Header Functions ---");
|
||||
|
||||
// Test 1.1: get_ledger_sqn() - should return current ledger sequence number
|
||||
let mut sqn_buffer = [0u8; 4];
|
||||
let sqn_result = unsafe { host::get_ledger_sqn(sqn_buffer.as_mut_ptr(), sqn_buffer.len()) };
|
||||
|
||||
if sqn_result <= 0 {
|
||||
let _ = trace_num("ERROR: get_ledger_sqn failed:", sqn_result as i64);
|
||||
return -101; // Ledger sequence number test failed
|
||||
}
|
||||
let ledger_sqn = u32::from_be_bytes(sqn_buffer);
|
||||
let _ = trace_num("Ledger sequence number:", ledger_sqn as i64);
|
||||
|
||||
// Test 1.2: get_parent_ledger_time() - should return parent ledger timestamp
|
||||
let mut time_buffer = [0u8; 4];
|
||||
let time_result =
|
||||
unsafe { host::get_parent_ledger_time(time_buffer.as_mut_ptr(), time_buffer.len()) };
|
||||
|
||||
if time_result <= 0 {
|
||||
let _ = trace_num("ERROR: get_parent_ledger_time failed:", time_result as i64);
|
||||
return -102; // Parent ledger time test failed
|
||||
}
|
||||
let parent_ledger_time = u32::from_be_bytes(time_buffer);
|
||||
let _ = trace_num("Parent ledger time:", parent_ledger_time as i64);
|
||||
|
||||
// Test 1.3: get_parent_ledger_hash() - should return parent ledger hash (32 bytes)
|
||||
let mut hash_buffer = [0u8; 32];
|
||||
let hash_result =
|
||||
unsafe { host::get_parent_ledger_hash(hash_buffer.as_mut_ptr(), hash_buffer.len()) };
|
||||
|
||||
if hash_result != 32 {
|
||||
let _ = trace_num(
|
||||
"ERROR: get_parent_ledger_hash wrong length:",
|
||||
hash_result as i64,
|
||||
);
|
||||
return -103; // Parent ledger hash test failed - should be exactly 32 bytes
|
||||
}
|
||||
let _ = trace_data("Parent ledger hash:", &hash_buffer, DataRepr::AsHex);
|
||||
|
||||
let _ = trace("SUCCESS: Ledger header functions");
|
||||
0
|
||||
}
|
||||
|
||||
/// Test Category 2: Transaction Data Functions (5 functions)
|
||||
/// Tests all functions for accessing current transaction data
|
||||
fn test_transaction_data_functions() -> i32 {
|
||||
let _ = trace("--- Category 2: Transaction Data Functions ---");
|
||||
|
||||
// Test 2.1: get_tx_field() - Basic transaction field access
|
||||
// Test with Account field (required, 20 bytes)
|
||||
let mut account_buffer = [0u8; 20];
|
||||
let account_len = unsafe {
|
||||
host::get_tx_field(
|
||||
sfield::Account,
|
||||
account_buffer.as_mut_ptr(),
|
||||
account_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if account_len != 20 {
|
||||
let _ = trace_num(
|
||||
"ERROR: get_tx_field(Account) wrong length:",
|
||||
account_len as i64,
|
||||
);
|
||||
return -201; // Basic transaction field test failed
|
||||
}
|
||||
let _ = trace_account_buf("Transaction Account:", &account_buffer);
|
||||
|
||||
// Test with Fee field (XRP amount - 8 bytes in new serialized format)
|
||||
// New format: XRP amounts are always 8 bytes (positive: value | cPositive flag, negative: just value)
|
||||
let mut fee_buffer = [0u8; 8];
|
||||
let fee_len =
|
||||
unsafe { host::get_tx_field(sfield::Fee, fee_buffer.as_mut_ptr(), fee_buffer.len()) };
|
||||
|
||||
if fee_len != 8 {
|
||||
let _ = trace_num(
|
||||
"ERROR: get_tx_field(Fee) wrong length (expected 8 bytes for XRP):",
|
||||
fee_len as i64,
|
||||
);
|
||||
return -202; // Fee field test failed - XRP amounts should be exactly 8 bytes
|
||||
}
|
||||
let _ = trace_num("Transaction Fee length:", fee_len as i64);
|
||||
let _ = trace_data(
|
||||
"Transaction Fee (serialized XRP amount):",
|
||||
&fee_buffer,
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
|
||||
// Test with Sequence field (required, 4 bytes uint32)
|
||||
let mut seq_buffer = [0u8; 4];
|
||||
let seq_len =
|
||||
unsafe { host::get_tx_field(sfield::Sequence, seq_buffer.as_mut_ptr(), seq_buffer.len()) };
|
||||
|
||||
if seq_len != 4 {
|
||||
let _ = trace_num(
|
||||
"ERROR: get_tx_field(Sequence) wrong length:",
|
||||
seq_len as i64,
|
||||
);
|
||||
return -203; // Sequence field test failed
|
||||
}
|
||||
let _ = trace_data("Transaction Sequence:", &seq_buffer, DataRepr::AsHex);
|
||||
|
||||
// NOTE: get_tx_field2() through get_tx_field6() have been deprecated.
|
||||
// Use get_tx_field() with appropriate parameters for all transaction field access.
|
||||
|
||||
// Test 2.2: get_tx_nested_field() - Nested field access with locator
|
||||
let locator = [0x01, 0x00]; // Simple locator for first element
|
||||
let mut nested_buffer = [0u8; 32];
|
||||
let nested_result = unsafe {
|
||||
host::get_tx_nested_field(
|
||||
locator.as_ptr(),
|
||||
locator.len(),
|
||||
nested_buffer.as_mut_ptr(),
|
||||
nested_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if nested_result < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_tx_nested_field not applicable:",
|
||||
nested_result as i64,
|
||||
);
|
||||
// Expected - locator may not match transaction structure
|
||||
} else {
|
||||
let _ = trace_num("Nested field length:", nested_result as i64);
|
||||
let _ = trace_data(
|
||||
"Nested field:",
|
||||
&nested_buffer[..nested_result as usize],
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
}
|
||||
|
||||
// Test 2.3: get_tx_array_len() - Get array length
|
||||
let signers_len = unsafe { host::get_tx_array_len(sfield::Signers) };
|
||||
let _ = trace_num("Signers array length:", signers_len as i64);
|
||||
|
||||
let memos_len = unsafe { host::get_tx_array_len(sfield::Memos) };
|
||||
let _ = trace_num("Memos array length:", memos_len as i64);
|
||||
|
||||
// Test 2.4: get_tx_nested_array_len() - Get nested array length with locator
|
||||
let nested_array_len =
|
||||
unsafe { host::get_tx_nested_array_len(locator.as_ptr(), locator.len()) };
|
||||
|
||||
if nested_array_len < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_tx_nested_array_len not applicable:",
|
||||
nested_array_len as i64,
|
||||
);
|
||||
} else {
|
||||
let _ = trace_num("Nested array length:", nested_array_len as i64);
|
||||
}
|
||||
|
||||
let _ = trace("SUCCESS: Transaction data functions");
|
||||
0
|
||||
}
|
||||
|
||||
/// Test Category 3: Current Ledger Object Functions (4 functions)
|
||||
/// Tests functions that access the current ledger object being processed
|
||||
fn test_current_ledger_object_functions() -> i32 {
|
||||
let _ = trace("--- Category 3: Current Ledger Object Functions ---");
|
||||
|
||||
// Test 3.1: get_current_ledger_obj_field() - Access field from current ledger object
|
||||
// Test with Balance field (XRP amount - 8 bytes in new serialized format)
|
||||
let mut balance_buffer = [0u8; 8];
|
||||
let balance_result = unsafe {
|
||||
host::get_current_ledger_obj_field(
|
||||
sfield::Balance,
|
||||
balance_buffer.as_mut_ptr(),
|
||||
balance_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if balance_result <= 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_current_ledger_obj_field(Balance) failed (may be expected):",
|
||||
balance_result as i64,
|
||||
);
|
||||
// This might fail if current ledger object doesn't have balance field
|
||||
} else if balance_result == 8 {
|
||||
let _ = trace_num(
|
||||
"Current object balance length (XRP amount):",
|
||||
balance_result as i64,
|
||||
);
|
||||
let _ = trace_data(
|
||||
"Current object balance (serialized XRP amount):",
|
||||
&balance_buffer,
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
} else {
|
||||
let _ = trace_num(
|
||||
"Current object balance length (non-XRP amount):",
|
||||
balance_result as i64,
|
||||
);
|
||||
let _ = trace_data(
|
||||
"Current object balance:",
|
||||
&balance_buffer[..balance_result as usize],
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
}
|
||||
|
||||
// Test with Account field
|
||||
let mut current_account_buffer = [0u8; 20];
|
||||
let current_account_result = unsafe {
|
||||
host::get_current_ledger_obj_field(
|
||||
sfield::Account,
|
||||
current_account_buffer.as_mut_ptr(),
|
||||
current_account_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if current_account_result <= 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_current_ledger_obj_field(Account) failed:",
|
||||
current_account_result as i64,
|
||||
);
|
||||
} else {
|
||||
let _ = trace_account_buf("Current ledger object account:", ¤t_account_buffer);
|
||||
}
|
||||
|
||||
// Test 3.2: get_current_ledger_obj_nested_field() - Nested field access
|
||||
let locator = [0x01, 0x00]; // Simple locator
|
||||
let mut current_nested_buffer = [0u8; 32];
|
||||
let current_nested_result = unsafe {
|
||||
host::get_current_ledger_obj_nested_field(
|
||||
locator.as_ptr(),
|
||||
locator.len(),
|
||||
current_nested_buffer.as_mut_ptr(),
|
||||
current_nested_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if current_nested_result < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_current_ledger_obj_nested_field not applicable:",
|
||||
current_nested_result as i64,
|
||||
);
|
||||
} else {
|
||||
let _ = trace_num("Current nested field length:", current_nested_result as i64);
|
||||
let _ = trace_data(
|
||||
"Current nested field:",
|
||||
¤t_nested_buffer[..current_nested_result as usize],
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
}
|
||||
|
||||
// Test 3.3: get_current_ledger_obj_array_len() - Array length in current object
|
||||
let current_array_len = unsafe { host::get_current_ledger_obj_array_len(sfield::Signers) };
|
||||
let _ = trace_num(
|
||||
"Current object Signers array length:",
|
||||
current_array_len as i64,
|
||||
);
|
||||
|
||||
// Test 3.4: get_current_ledger_obj_nested_array_len() - Nested array length
|
||||
let current_nested_array_len =
|
||||
unsafe { host::get_current_ledger_obj_nested_array_len(locator.as_ptr(), locator.len()) };
|
||||
|
||||
if current_nested_array_len < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_current_ledger_obj_nested_array_len not applicable:",
|
||||
current_nested_array_len as i64,
|
||||
);
|
||||
} else {
|
||||
let _ = trace_num(
|
||||
"Current nested array length:",
|
||||
current_nested_array_len as i64,
|
||||
);
|
||||
}
|
||||
|
||||
let _ = trace("SUCCESS: Current ledger object functions");
|
||||
0
|
||||
}
|
||||
|
||||
/// Test Category 4: Any Ledger Object Functions (5 functions)
|
||||
/// Tests functions that work with cached ledger objects
|
||||
fn test_any_ledger_object_functions() -> i32 {
|
||||
let _ = trace("--- Category 4: Any Ledger Object Functions ---");
|
||||
|
||||
// First we need to cache a ledger object to test the other functions
|
||||
// Get the account from transaction and generate its keylet
|
||||
let escrow_finish = EscrowFinish;
|
||||
let account_id = escrow_finish.get_account().unwrap();
|
||||
|
||||
// Test 4.1: cache_ledger_obj() - Cache a ledger object
|
||||
let mut keylet_buffer = [0u8; 32];
|
||||
let keylet_result = unsafe {
|
||||
host::account_keylet(
|
||||
account_id.0.as_ptr(),
|
||||
account_id.0.len(),
|
||||
keylet_buffer.as_mut_ptr(),
|
||||
keylet_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if keylet_result != 32 {
|
||||
let _ = trace_num(
|
||||
"ERROR: account_keylet failed for caching test:",
|
||||
keylet_result as i64,
|
||||
);
|
||||
return -401; // Keylet generation failed for caching test
|
||||
}
|
||||
|
||||
let cache_result =
|
||||
unsafe { host::cache_ledger_obj(keylet_buffer.as_ptr(), keylet_result as usize, 0) };
|
||||
|
||||
if cache_result <= 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: cache_ledger_obj failed (expected with test fixtures):",
|
||||
cache_result as i64,
|
||||
);
|
||||
// Test fixtures may not contain the account object - this is expected
|
||||
// We'll test the interface but expect failures
|
||||
|
||||
// Test 4.2-4.5 with invalid slot (should fail gracefully)
|
||||
let mut test_buffer = [0u8; 32];
|
||||
|
||||
// Test get_ledger_obj_field with invalid slot
|
||||
let field_result = unsafe {
|
||||
host::get_ledger_obj_field(
|
||||
1,
|
||||
sfield::Balance,
|
||||
test_buffer.as_mut_ptr(),
|
||||
test_buffer.len(),
|
||||
)
|
||||
};
|
||||
if field_result < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_ledger_obj_field failed as expected (no cached object):",
|
||||
field_result as i64,
|
||||
);
|
||||
}
|
||||
|
||||
// Test get_ledger_obj_nested_field with invalid slot
|
||||
let locator = [0x01, 0x00];
|
||||
let nested_result = unsafe {
|
||||
host::get_ledger_obj_nested_field(
|
||||
1,
|
||||
locator.as_ptr(),
|
||||
locator.len(),
|
||||
test_buffer.as_mut_ptr(),
|
||||
test_buffer.len(),
|
||||
)
|
||||
};
|
||||
if nested_result < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_ledger_obj_nested_field failed as expected:",
|
||||
nested_result as i64,
|
||||
);
|
||||
}
|
||||
|
||||
// Test get_ledger_obj_array_len with invalid slot
|
||||
let array_result = unsafe { host::get_ledger_obj_array_len(1, sfield::Signers) };
|
||||
if array_result < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_ledger_obj_array_len failed as expected:",
|
||||
array_result as i64,
|
||||
);
|
||||
}
|
||||
|
||||
// Test get_ledger_obj_nested_array_len with invalid slot
|
||||
let nested_array_result =
|
||||
unsafe { host::get_ledger_obj_nested_array_len(1, locator.as_ptr(), locator.len()) };
|
||||
if nested_array_result < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_ledger_obj_nested_array_len failed as expected:",
|
||||
nested_array_result as i64,
|
||||
);
|
||||
}
|
||||
|
||||
let _ = trace("SUCCESS: Any ledger object functions (interface tested)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we successfully cached an object, test the access functions
|
||||
let slot = cache_result;
|
||||
let _ = trace_num("Successfully cached object in slot:", slot as i64);
|
||||
|
||||
// Test 4.2: get_ledger_obj_field() - Access field from cached object
|
||||
let mut cached_balance_buffer = [0u8; 8];
|
||||
let cached_balance_result = unsafe {
|
||||
host::get_ledger_obj_field(
|
||||
slot,
|
||||
sfield::Balance,
|
||||
cached_balance_buffer.as_mut_ptr(),
|
||||
cached_balance_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if cached_balance_result <= 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_ledger_obj_field(Balance) failed:",
|
||||
cached_balance_result as i64,
|
||||
);
|
||||
} else if cached_balance_result == 8 {
|
||||
let _ = trace_num(
|
||||
"Cached object balance length (XRP amount):",
|
||||
cached_balance_result as i64,
|
||||
);
|
||||
let _ = trace_data(
|
||||
"Cached object balance (serialized XRP amount):",
|
||||
&cached_balance_buffer,
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
} else {
|
||||
let _ = trace_num(
|
||||
"Cached object balance length (non-XRP amount):",
|
||||
cached_balance_result as i64,
|
||||
);
|
||||
let _ = trace_data(
|
||||
"Cached object balance:",
|
||||
&cached_balance_buffer[..cached_balance_result as usize],
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
}
|
||||
|
||||
// Test 4.3: get_ledger_obj_nested_field() - Nested field from cached object
|
||||
let locator = [0x01, 0x00];
|
||||
let mut cached_nested_buffer = [0u8; 32];
|
||||
let cached_nested_result = unsafe {
|
||||
host::get_ledger_obj_nested_field(
|
||||
slot,
|
||||
locator.as_ptr(),
|
||||
locator.len(),
|
||||
cached_nested_buffer.as_mut_ptr(),
|
||||
cached_nested_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if cached_nested_result < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_ledger_obj_nested_field not applicable:",
|
||||
cached_nested_result as i64,
|
||||
);
|
||||
} else {
|
||||
let _ = trace_num("Cached nested field length:", cached_nested_result as i64);
|
||||
let _ = trace_data(
|
||||
"Cached nested field:",
|
||||
&cached_nested_buffer[..cached_nested_result as usize],
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
}
|
||||
|
||||
// Test 4.4: get_ledger_obj_array_len() - Array length from cached object
|
||||
let cached_array_len = unsafe { host::get_ledger_obj_array_len(slot, sfield::Signers) };
|
||||
let _ = trace_num(
|
||||
"Cached object Signers array length:",
|
||||
cached_array_len as i64,
|
||||
);
|
||||
|
||||
// Test 4.5: get_ledger_obj_nested_array_len() - Nested array length from cached object
|
||||
let cached_nested_array_len =
|
||||
unsafe { host::get_ledger_obj_nested_array_len(slot, locator.as_ptr(), locator.len()) };
|
||||
|
||||
if cached_nested_array_len < 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_ledger_obj_nested_array_len not applicable:",
|
||||
cached_nested_array_len as i64,
|
||||
);
|
||||
} else {
|
||||
let _ = trace_num(
|
||||
"Cached nested array length:",
|
||||
cached_nested_array_len as i64,
|
||||
);
|
||||
}
|
||||
|
||||
let _ = trace("SUCCESS: Any ledger object functions");
|
||||
0
|
||||
}
|
||||
|
||||
/// Test Category 5: Keylet Generation Functions (4 functions)
|
||||
/// Tests keylet generation functions for different ledger entry types
|
||||
fn test_keylet_generation_functions() -> i32 {
|
||||
let _ = trace("--- Category 5: Keylet Generation Functions ---");
|
||||
|
||||
let escrow_finish = EscrowFinish;
|
||||
let account_id = escrow_finish.get_account().unwrap();
|
||||
|
||||
// Test 5.1: account_keylet() - Generate keylet for account
|
||||
let mut account_keylet_buffer = [0u8; 32];
|
||||
let account_keylet_result = unsafe {
|
||||
host::account_keylet(
|
||||
account_id.0.as_ptr(),
|
||||
account_id.0.len(),
|
||||
account_keylet_buffer.as_mut_ptr(),
|
||||
account_keylet_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if account_keylet_result != 32 {
|
||||
let _ = trace_num(
|
||||
"ERROR: account_keylet failed:",
|
||||
account_keylet_result as i64,
|
||||
);
|
||||
return -501; // Account keylet generation failed
|
||||
}
|
||||
let _ = trace_data("Account keylet:", &account_keylet_buffer, DataRepr::AsHex);
|
||||
|
||||
// Test 5.2: credential_keylet() - Generate keylet for credential
|
||||
let mut credential_keylet_buffer = [0u8; 32];
|
||||
let credential_keylet_result = unsafe {
|
||||
host::credential_keylet(
|
||||
account_id.0.as_ptr(), // Subject
|
||||
account_id.0.len(),
|
||||
account_id.0.as_ptr(), // Issuer - same account for test
|
||||
account_id.0.len(),
|
||||
b"TestType".as_ptr(), // Credential type
|
||||
9usize, // Length of "TestType"
|
||||
credential_keylet_buffer.as_mut_ptr(),
|
||||
credential_keylet_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if credential_keylet_result <= 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: credential_keylet failed (expected - interface issue):",
|
||||
credential_keylet_result as i64,
|
||||
);
|
||||
// This is expected to fail due to unusual parameter types
|
||||
} else {
|
||||
let _ = trace_data(
|
||||
"Credential keylet:",
|
||||
&credential_keylet_buffer[..credential_keylet_result as usize],
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
}
|
||||
|
||||
// Test 5.3: escrow_keylet() - Generate keylet for escrow
|
||||
let mut escrow_keylet_buffer = [0u8; 32];
|
||||
let sequence_number: i32 = 1000;
|
||||
let sequence_number_bytes = sequence_number.to_be_bytes();
|
||||
let escrow_keylet_result = unsafe {
|
||||
host::escrow_keylet(
|
||||
account_id.0.as_ptr(),
|
||||
account_id.0.len(),
|
||||
sequence_number_bytes.as_ptr(),
|
||||
sequence_number_bytes.len(),
|
||||
escrow_keylet_buffer.as_mut_ptr(),
|
||||
escrow_keylet_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if escrow_keylet_result != 32 {
|
||||
let _ = trace_num("ERROR: escrow_keylet failed:", escrow_keylet_result as i64);
|
||||
return -503; // Escrow keylet generation failed
|
||||
}
|
||||
let _ = trace_data("Escrow keylet:", &escrow_keylet_buffer, DataRepr::AsHex);
|
||||
|
||||
// Test 5.4: oracle_keylet() - Generate keylet for oracle
|
||||
let mut oracle_keylet_buffer = [0u8; 32];
|
||||
let document_id: i32 = 42;
|
||||
let document_id_bytes = document_id.to_be_bytes();
|
||||
let oracle_keylet_result = unsafe {
|
||||
host::oracle_keylet(
|
||||
account_id.0.as_ptr(),
|
||||
account_id.0.len(),
|
||||
document_id_bytes.as_ptr(),
|
||||
document_id_bytes.len(),
|
||||
oracle_keylet_buffer.as_mut_ptr(),
|
||||
oracle_keylet_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if oracle_keylet_result != 32 {
|
||||
let _ = trace_num("ERROR: oracle_keylet failed:", oracle_keylet_result as i64);
|
||||
return -504; // Oracle keylet generation failed
|
||||
}
|
||||
let _ = trace_data("Oracle keylet:", &oracle_keylet_buffer, DataRepr::AsHex);
|
||||
|
||||
let _ = trace("SUCCESS: Keylet generation functions");
|
||||
0
|
||||
}
|
||||
|
||||
/// Test Category 6: Utility Functions (4 functions)
|
||||
/// Tests utility functions for hashing, NFT access, and tracing
|
||||
fn test_utility_functions() -> i32 {
|
||||
let _ = trace("--- Category 6: Utility Functions ---");
|
||||
|
||||
// Test 6.1: compute_sha512_half() - SHA512 hash computation (first 32 bytes)
|
||||
let test_data = b"Hello, XRPL WASM world!";
|
||||
let mut hash_output = [0u8; 32];
|
||||
let hash_result = unsafe {
|
||||
host::compute_sha512_half(
|
||||
test_data.as_ptr(),
|
||||
test_data.len(),
|
||||
hash_output.as_mut_ptr(),
|
||||
hash_output.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if hash_result != 32 {
|
||||
let _ = trace_num("ERROR: compute_sha512_half failed:", hash_result as i64);
|
||||
return -601; // SHA512 half computation failed
|
||||
}
|
||||
let _ = trace_data("Input data:", test_data, DataRepr::AsHex);
|
||||
let _ = trace_data("SHA512 half hash:", &hash_output, DataRepr::AsHex);
|
||||
|
||||
// Test 6.2: get_nft() - NFT data retrieval
|
||||
let escrow_finish = EscrowFinish;
|
||||
let account_id = escrow_finish.get_account().unwrap();
|
||||
let nft_id = [0u8; 32]; // Dummy NFT ID for testing
|
||||
let mut nft_buffer = [0u8; 256];
|
||||
let nft_result = unsafe {
|
||||
host::get_nft(
|
||||
account_id.0.as_ptr(),
|
||||
account_id.0.len(),
|
||||
nft_id.as_ptr(),
|
||||
nft_id.len(),
|
||||
nft_buffer.as_mut_ptr(),
|
||||
nft_buffer.len(),
|
||||
)
|
||||
};
|
||||
|
||||
if nft_result <= 0 {
|
||||
let _ = trace_num(
|
||||
"INFO: get_nft failed (expected - no such NFT):",
|
||||
nft_result as i64,
|
||||
);
|
||||
// This is expected - test account likely doesn't own the dummy NFT
|
||||
} else {
|
||||
let _ = trace_num("NFT data length:", nft_result as i64);
|
||||
let _ = trace_data(
|
||||
"NFT data:",
|
||||
&nft_buffer[..nft_result as usize],
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
}
|
||||
|
||||
// Test 6.3: trace() - Debug logging with data
|
||||
let trace_message = b"Test trace message";
|
||||
let trace_data_payload = b"payload";
|
||||
let trace_result = unsafe {
|
||||
host::trace(
|
||||
trace_message.as_ptr(),
|
||||
trace_message.len(),
|
||||
trace_data_payload.as_ptr(),
|
||||
trace_data_payload.len(),
|
||||
1, // as_hex = true
|
||||
)
|
||||
};
|
||||
|
||||
if trace_result < 0 {
|
||||
let _ = trace_num("ERROR: trace() failed:", trace_result as i64);
|
||||
return -603; // Trace function failed
|
||||
}
|
||||
let _ = trace_num("Trace function bytes written:", trace_result as i64);
|
||||
|
||||
// Test 6.4: trace_num() - Debug logging with number
|
||||
let test_number = 42i64;
|
||||
let trace_num_result = trace_num("Test number trace", test_number);
|
||||
|
||||
use xrpl_std::host::Result;
|
||||
match trace_num_result {
|
||||
Result::Ok(_) => {
|
||||
let _ = trace_num("Trace_num function succeeded", 0);
|
||||
}
|
||||
Result::Err(_) => {
|
||||
let _ = trace_num("ERROR: trace_num() failed:", -604);
|
||||
return -604; // Trace number function failed
|
||||
}
|
||||
}
|
||||
|
||||
let _ = trace("SUCCESS: Utility functions");
|
||||
0
|
||||
}
|
||||
|
||||
/// Test Category 7: Data Update Functions (1 function)
|
||||
/// Tests the function for modifying the current ledger entry
|
||||
fn test_data_update_functions() -> i32 {
|
||||
let _ = trace("--- Category 7: Data Update Functions ---");
|
||||
|
||||
// Test 7.1: update_data() - Update current ledger entry data
|
||||
let update_payload = b"Updated ledger entry data from WASM test";
|
||||
|
||||
let update_result = unsafe { host::update_data(update_payload.as_ptr(), update_payload.len()) };
|
||||
|
||||
if update_result != update_payload.len() as i32 {
|
||||
let _ = trace_num("ERROR: update_data failed:", update_result as i64);
|
||||
return -701; // Data update failed
|
||||
}
|
||||
|
||||
let _ = trace_data(
|
||||
"Successfully updated ledger entry with:",
|
||||
update_payload,
|
||||
DataRepr::AsHex,
|
||||
);
|
||||
let _ = trace("SUCCESS: Data update functions");
|
||||
0
|
||||
}
|
||||
171
src/test/app/wasm_fixtures/all_keylets/Cargo.lock
generated
Normal file
171
src/test/app/wasm_fixtures/all_keylets/Cargo.lock
generated
Normal file
@@ -0,0 +1,171 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "all_keylets"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"xrpl-wasm-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "xrpl-address-macro"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=u32-buffer#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"quote",
|
||||
"sha2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xrpl-wasm-stdlib"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=u32-buffer#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8"
|
||||
dependencies = [
|
||||
"xrpl-address-macro",
|
||||
]
|
||||
21
src/test/app/wasm_fixtures/all_keylets/Cargo.toml
Normal file
21
src/test/app/wasm_fixtures/all_keylets/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
edition = "2024"
|
||||
name = "all_keylets"
|
||||
version = "0.0.1"
|
||||
|
||||
# This empty workspace definition keeps this project independent of the parent workspace
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 's'
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
xrpl-std = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-wasm-stdlib", branch = "u32-buffer" }
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
181
src/test/app/wasm_fixtures/all_keylets/src/lib.rs
Normal file
181
src/test/app/wasm_fixtures/all_keylets/src/lib.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
#![cfg_attr(target_arch = "wasm32", no_std)]
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
extern crate std;
|
||||
|
||||
use crate::host::{Error, Result, Result::Err, Result::Ok};
|
||||
use xrpl_std::core::ledger_objects::current_escrow::get_current_escrow;
|
||||
use xrpl_std::core::ledger_objects::current_escrow::CurrentEscrow;
|
||||
use xrpl_std::core::ledger_objects::ledger_object;
|
||||
use xrpl_std::core::ledger_objects::traits::CurrentEscrowFields;
|
||||
use xrpl_std::core::types::account_id::AccountID;
|
||||
use xrpl_std::core::types::currency::Currency;
|
||||
use xrpl_std::core::types::issue::{IouIssue, Issue, XrpIssue};
|
||||
use xrpl_std::core::types::keylets;
|
||||
use xrpl_std::core::types::mpt_id::MptId;
|
||||
use xrpl_std::core::types::uint::Hash256;
|
||||
use xrpl_std::host;
|
||||
use xrpl_std::host::trace::{trace, trace_account, trace_data, trace_num, DataRepr};
|
||||
use xrpl_std::sfield;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn object_exists(
|
||||
keylet_result: Result<keylets::KeyletBytes>,
|
||||
keylet_type: &str,
|
||||
field: i32,
|
||||
) -> Result<bool> {
|
||||
match keylet_result {
|
||||
Ok(keylet) => {
|
||||
let _ = trace_data(keylet_type, &keylet, DataRepr::AsHex);
|
||||
|
||||
let slot = unsafe { host::cache_ledger_obj(keylet.as_ptr(), keylet.len(), 0) };
|
||||
if slot <= 0 {
|
||||
let _ = trace_num("Error: ", slot.into());
|
||||
return Err(Error::from_code(slot));
|
||||
}
|
||||
if field == 0 {
|
||||
let new_field = sfield::PreviousTxnID;
|
||||
let _ = trace_num("Getting field: ", new_field.into());
|
||||
match ledger_object::get_field::<Hash256>(slot, new_field) {
|
||||
Ok(data) => {
|
||||
let _ = trace_data("Field data: ", &data.0, DataRepr::AsHex);
|
||||
}
|
||||
Err(result_code) => {
|
||||
let _ = trace_num("Error getting field: ", result_code.into());
|
||||
return Err(result_code);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = trace_num("Getting field: ", field.into());
|
||||
match ledger_object::get_field::<AccountID>(slot, field) {
|
||||
Ok(data) => {
|
||||
let _ = trace_data("Field data: ", &data.0, DataRepr::AsHex);
|
||||
}
|
||||
Err(result_code) => {
|
||||
let _ = trace_num("Error getting field: ", result_code.into());
|
||||
return Err(result_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
Err(error) => {
|
||||
let _ = trace_num("Error getting keylet: ", error.into());
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn finish() -> i32 {
|
||||
let _ = trace("$$$$$ STARTING WASM EXECUTION $$$$$");
|
||||
|
||||
let escrow: CurrentEscrow = get_current_escrow();
|
||||
|
||||
let account = escrow.get_account().unwrap_or_panic();
|
||||
let _ = trace_account("Account:", &account);
|
||||
|
||||
let destination = escrow.get_destination().unwrap_or_panic();
|
||||
let _ = trace_account("Destination:", &destination);
|
||||
|
||||
let mut seq = 5;
|
||||
|
||||
macro_rules! check_object_exists {
|
||||
($keylet:expr, $type:expr, $field:expr) => {
|
||||
match object_exists($keylet, $type, $field) {
|
||||
Ok(_exists) => {
|
||||
// false isn't returned
|
||||
let _ = trace(concat!(
|
||||
$type,
|
||||
" object exists, proceeding with escrow finish."
|
||||
));
|
||||
}
|
||||
Err(error) => {
|
||||
let _ = trace_num("Current seq value:", seq.try_into().unwrap());
|
||||
return error.code();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let account_keylet = keylets::account_keylet(&account);
|
||||
check_object_exists!(account_keylet, "Account", sfield::Account);
|
||||
|
||||
let currency_code: &[u8; 3] = b"USD";
|
||||
let currency: Currency = Currency::from(*currency_code);
|
||||
let line_keylet = keylets::line_keylet(&account, &destination, ¤cy);
|
||||
check_object_exists!(line_keylet, "Trustline", sfield::Generic);
|
||||
seq += 1;
|
||||
|
||||
let asset1 = Issue::XRP(XrpIssue {});
|
||||
let asset2 = Issue::IOU(IouIssue::new(destination, currency));
|
||||
check_object_exists!(
|
||||
keylets::amm_keylet(&asset1, &asset2),
|
||||
"AMM",
|
||||
sfield::Account
|
||||
);
|
||||
|
||||
let check_keylet = keylets::check_keylet(&account, seq);
|
||||
check_object_exists!(check_keylet, "Check", sfield::Account);
|
||||
seq += 1;
|
||||
|
||||
let cred_type: &[u8] = b"termsandconditions";
|
||||
let credential_keylet = keylets::credential_keylet(&account, &account, cred_type);
|
||||
check_object_exists!(credential_keylet, "Credential", sfield::Subject);
|
||||
seq += 1;
|
||||
|
||||
let delegate_keylet = keylets::delegate_keylet(&account, &destination);
|
||||
check_object_exists!(delegate_keylet, "Delegate", sfield::Account);
|
||||
seq += 1;
|
||||
|
||||
let deposit_preauth_keylet = keylets::deposit_preauth_keylet(&account, &destination);
|
||||
check_object_exists!(deposit_preauth_keylet, "DepositPreauth", sfield::Account);
|
||||
seq += 1;
|
||||
|
||||
let did_keylet = keylets::did_keylet(&account);
|
||||
check_object_exists!(did_keylet, "DID", sfield::Account);
|
||||
seq += 1;
|
||||
|
||||
let escrow_keylet = keylets::escrow_keylet(&account, seq);
|
||||
check_object_exists!(escrow_keylet, "Escrow", sfield::Account);
|
||||
seq += 1;
|
||||
|
||||
let mpt_issuance_keylet = keylets::mpt_issuance_keylet(&account, seq);
|
||||
let mpt_id = MptId::new(seq.try_into().unwrap(), account);
|
||||
check_object_exists!(mpt_issuance_keylet, "MPTIssuance", sfield::Issuer);
|
||||
seq += 1;
|
||||
|
||||
let mptoken_keylet = keylets::mptoken_keylet(&mpt_id, &destination);
|
||||
check_object_exists!(mptoken_keylet, "MPToken", sfield::Account);
|
||||
|
||||
let nft_offer_keylet = keylets::nft_offer_keylet(&destination, 6);
|
||||
check_object_exists!(nft_offer_keylet, "NFTokenOffer", sfield::Owner);
|
||||
|
||||
let offer_keylet = keylets::offer_keylet(&account, seq);
|
||||
check_object_exists!(offer_keylet, "Offer", sfield::Account);
|
||||
seq += 1;
|
||||
|
||||
let paychan_keylet = keylets::paychan_keylet(&account, &destination, seq);
|
||||
check_object_exists!(paychan_keylet, "PayChannel", sfield::Account);
|
||||
seq += 1;
|
||||
|
||||
let pd_keylet = keylets::permissioned_domain_keylet(&account, seq);
|
||||
check_object_exists!(pd_keylet, "PermissionedDomain", sfield::Owner);
|
||||
seq += 1;
|
||||
|
||||
let signers_keylet = keylets::signers_keylet(&account);
|
||||
check_object_exists!(signers_keylet, "SignerList", sfield::Generic);
|
||||
seq += 1;
|
||||
|
||||
seq += 1; // ticket sequence number is one greater
|
||||
let ticket_keylet = keylets::ticket_keylet(&account, seq);
|
||||
check_object_exists!(ticket_keylet, "Ticket", sfield::Account);
|
||||
seq += 1;
|
||||
|
||||
let vault_keylet = keylets::vault_keylet(&account, seq);
|
||||
check_object_exists!(vault_keylet, "Vault", sfield::Account);
|
||||
// seq += 1;
|
||||
|
||||
1 // All keylets exist, finish the escrow.
|
||||
}
|
||||
72
src/test/app/wasm_fixtures/b58.c
Normal file
72
src/test/app/wasm_fixtures/b58.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <stdint.h>
|
||||
|
||||
static char const b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
uint8_t e_data[32 * 1024];
|
||||
|
||||
void*
|
||||
allocate(int sz)
|
||||
{
|
||||
static int idx = 0;
|
||||
if (idx >= 32)
|
||||
return 0;
|
||||
if (sz > 1024)
|
||||
return 0;
|
||||
return &e_data[idx++ << 10];
|
||||
}
|
||||
|
||||
void
|
||||
deallocate(void* p)
|
||||
{
|
||||
}
|
||||
|
||||
extern int32_t
|
||||
b58enco(char* b58, int32_t b58sz, void const* data, int32_t binsz)
|
||||
{
|
||||
uint8_t const* bin = data;
|
||||
int32_t carry;
|
||||
int32_t i, j, high, zcount = 0;
|
||||
int32_t size;
|
||||
|
||||
while (zcount < binsz && !bin[zcount])
|
||||
++zcount;
|
||||
|
||||
size = (binsz - zcount) * 138 / 100 + 1;
|
||||
uint8_t* buf = allocate(size);
|
||||
if (!buf)
|
||||
return 0;
|
||||
// memset(buf, 0, size);
|
||||
for (i = 0; i < size; ++i)
|
||||
buf[i] = 0;
|
||||
|
||||
for (i = zcount, high = size - 1; i < binsz; ++i, high = j)
|
||||
{
|
||||
for (carry = bin[i], j = size - 1; (j > high) || carry; --j)
|
||||
{
|
||||
carry += 256 * buf[j];
|
||||
buf[j] = carry % 58;
|
||||
carry /= 58;
|
||||
if (!j)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < size && !buf[j]; ++j)
|
||||
;
|
||||
|
||||
if (b58sz <= zcount + size - j)
|
||||
return 0;
|
||||
|
||||
if (zcount)
|
||||
{
|
||||
// memset(b58, '1', zcount);
|
||||
for (i = 0; i < zcount; ++i)
|
||||
b58[i] = '1';
|
||||
}
|
||||
|
||||
for (i = zcount; j < size; ++i, ++j)
|
||||
b58[i] = b58digits_ordered[buf[j]];
|
||||
b58[i] = '\0';
|
||||
|
||||
return i + 1;
|
||||
}
|
||||
47
src/test/app/wasm_fixtures/bad_align.c
Normal file
47
src/test/app/wasm_fixtures/bad_align.c
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <stdint.h>
|
||||
|
||||
int32_t
|
||||
float_from_uint(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
int32_t
|
||||
check_keylet(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
|
||||
uint8_t e_data1[32 * 1024];
|
||||
uint8_t e_data2[32 * 1024];
|
||||
|
||||
int32_t
|
||||
test1()
|
||||
{
|
||||
e_data1[1] = 0xFF;
|
||||
e_data1[2] = 0xFF;
|
||||
e_data1[3] = 0xFF;
|
||||
e_data1[4] = 0xFF;
|
||||
e_data1[5] = 0xFF;
|
||||
e_data1[6] = 0xFF;
|
||||
e_data1[7] = 0xFF;
|
||||
e_data1[8] = 0xFF;
|
||||
int32_t result = float_from_uint(&e_data1[1], 8, &e_data1[35], 12, 0);
|
||||
return result >= 0 ? *((int32_t*)(&e_data1[36])) : result;
|
||||
}
|
||||
|
||||
int32_t
|
||||
test2()
|
||||
{
|
||||
// Set up misaligned uint32 (seq) at offset 1
|
||||
e_data2[1] = 0xFF;
|
||||
e_data2[2] = 0xFF;
|
||||
e_data2[3] = 0xFF;
|
||||
e_data2[4] = 0xFF;
|
||||
// Set up valid non-zero AccountID (20 bytes) at offset 10
|
||||
for (int i = 0; i < 20; i++)
|
||||
e_data2[10 + i] = i + 1;
|
||||
// Call check_keylet with misaligned uint32 at &e_data2[1] to hit line 72 in HostFuncWrapper.cpp
|
||||
int32_t result = check_keylet(&e_data2[10], 20, &e_data2[1], 4, &e_data2[35], 32);
|
||||
// Return the misaligned value directly to validate it was read correctly (-1 if all 0xFF)
|
||||
return result >= 0 ? *((int32_t*)(&e_data2[36])) : result;
|
||||
}
|
||||
|
||||
int32_t
|
||||
test()
|
||||
{
|
||||
return test1() + test2();
|
||||
}
|
||||
27
src/test/app/wasm_fixtures/bad_alloc.c
Normal file
27
src/test/app/wasm_fixtures/bad_alloc.c
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <stdint.h>
|
||||
|
||||
char buf[1024];
|
||||
|
||||
void*
|
||||
allocate(int sz)
|
||||
{
|
||||
if (!sz)
|
||||
return 0;
|
||||
|
||||
if (sz == 1)
|
||||
return ((void*)(8 * 1024 * 1024));
|
||||
if (sz == 2)
|
||||
return 0;
|
||||
if (sz == 3)
|
||||
return ((void*)(0xFFFFFFFF));
|
||||
|
||||
return &buf[sz];
|
||||
}
|
||||
|
||||
int32_t
|
||||
test(char* p, int32_t sz)
|
||||
{
|
||||
if (!sz)
|
||||
return 0;
|
||||
return p[0];
|
||||
}
|
||||
171
src/test/app/wasm_fixtures/codecov_tests/Cargo.lock
generated
Normal file
171
src/test/app/wasm_fixtures/codecov_tests/Cargo.lock
generated
Normal file
@@ -0,0 +1,171 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "codecov_tests"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"xrpl-wasm-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "xrpl-address-macro"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=u32-buffer#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"quote",
|
||||
"sha2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xrpl-wasm-stdlib"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=u32-buffer#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8"
|
||||
dependencies = [
|
||||
"xrpl-address-macro",
|
||||
]
|
||||
18
src/test/app/wasm_fixtures/codecov_tests/Cargo.toml
Normal file
18
src/test/app/wasm_fixtures/codecov_tests/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
edition = "2024"
|
||||
name = "codecov_tests"
|
||||
version = "0.0.1"
|
||||
|
||||
# This empty workspace definition keeps this project independent of the parent workspace
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 's'
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
xrpl-std = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-wasm-stdlib", branch = "u32-buffer" }
|
||||
@@ -0,0 +1,47 @@
|
||||
//TODO add docs after discussing the interface
|
||||
//Note that Craft currently does not honor the rounding modes
|
||||
#[allow(unused)]
|
||||
pub const FLOAT_ROUNDING_MODES_TO_NEAREST: i32 = 0;
|
||||
#[allow(unused)]
|
||||
pub const FLOAT_ROUNDING_MODES_TOWARDS_ZERO: i32 = 1;
|
||||
#[allow(unused)]
|
||||
pub const FLOAT_ROUNDING_MODES_DOWNWARD: i32 = 2;
|
||||
#[allow(unused)]
|
||||
pub const FLOAT_ROUNDING_MODES_UPWARD: i32 = 3;
|
||||
|
||||
// pub enum RippledRoundingModes{
|
||||
// ToNearest = 0,
|
||||
// TowardsZero = 1,
|
||||
// DOWNWARD = 2,
|
||||
// UPWARD = 3
|
||||
// }
|
||||
|
||||
#[allow(unused)]
|
||||
#[link(wasm_import_module = "host_lib")]
|
||||
unsafe extern "C" {
|
||||
pub fn get_parent_ledger_hash(out_buff_ptr: i32, out_buff_len: i32) -> i32;
|
||||
|
||||
pub fn cache_ledger_obj(keylet_ptr: i32, keylet_len: i32, cache_num: i32) -> i32;
|
||||
|
||||
pub fn get_tx_nested_array_len(locator_ptr: i32, locator_len: i32) -> i32;
|
||||
|
||||
pub fn account_keylet(
|
||||
account_ptr: i32,
|
||||
account_len: i32,
|
||||
out_buff_ptr: *mut u8,
|
||||
out_buff_len: usize,
|
||||
) -> i32;
|
||||
|
||||
pub fn line_keylet(
|
||||
account1_ptr: *const u8,
|
||||
account1_len: usize,
|
||||
account2_ptr: *const u8,
|
||||
account2_len: usize,
|
||||
currency_ptr: i32,
|
||||
currency_len: i32,
|
||||
out_buff_ptr: *mut u8,
|
||||
out_buff_len: usize,
|
||||
) -> i32;
|
||||
|
||||
pub fn trace_num(msg_read_ptr: i32, msg_read_len: i32, number: i64) -> i32;
|
||||
}
|
||||
1797
src/test/app/wasm_fixtures/codecov_tests/src/lib.rs
Normal file
1797
src/test/app/wasm_fixtures/codecov_tests/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
124
src/test/app/wasm_fixtures/copyFixtures.py
Normal file
124
src/test/app/wasm_fixtures/copyFixtures.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
OPT = "-Oz"
|
||||
|
||||
|
||||
def update_fixture(project_name, wasm):
|
||||
fixture_name = (
|
||||
re.sub(r"_([a-z])", lambda m: m.group(1).upper(), project_name) + "WasmHex"
|
||||
)
|
||||
print(f"Updating fixture: {fixture_name}")
|
||||
|
||||
cpp_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "fixtures.cpp"))
|
||||
h_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "fixtures.h"))
|
||||
with open(cpp_path, "r", encoding="utf8") as f:
|
||||
cpp_content = f.read()
|
||||
|
||||
pattern = rf'extern std::string const {fixture_name} =[ \n]+"[^;]*;'
|
||||
if re.search(pattern, cpp_content, flags=re.MULTILINE):
|
||||
updated_cpp_content = re.sub(
|
||||
pattern,
|
||||
f'extern std::string const {fixture_name} = "{wasm}";',
|
||||
cpp_content,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
else:
|
||||
with open(h_path, "r", encoding="utf8") as f:
|
||||
h_content = f.read()
|
||||
updated_h_content = (
|
||||
h_content.rstrip() + f"\n\n extern std::string const {fixture_name};\n"
|
||||
)
|
||||
with open(h_path, "w", encoding="utf8") as f:
|
||||
f.write(updated_h_content)
|
||||
updated_cpp_content = (
|
||||
cpp_content.rstrip()
|
||||
+ f'\n\nextern std::string const {fixture_name} = "{wasm}";\n'
|
||||
)
|
||||
|
||||
with open(cpp_path, "w", encoding="utf8") as f:
|
||||
f.write(updated_cpp_content)
|
||||
|
||||
|
||||
def process_rust(project_name):
|
||||
project_path = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), project_name)
|
||||
)
|
||||
wasm_location = f"target/wasm32v1-none/release/{project_name}.wasm"
|
||||
build_cmd = (
|
||||
f"(cd {project_path} "
|
||||
f"&& cargo build --target wasm32v1-none --release "
|
||||
f"&& wasm-opt {wasm_location} {OPT} -o {wasm_location}"
|
||||
")"
|
||||
)
|
||||
try:
|
||||
subprocess.run(build_cmd, shell=True, check=True)
|
||||
print(f"WASM file for {project_name} has been built and optimized.")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"exec error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
src_path = os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
f"{project_name}/target/wasm32v1-none/release/{project_name}.wasm",
|
||||
)
|
||||
)
|
||||
with open(src_path, "rb") as f:
|
||||
data = f.read()
|
||||
wasm = data.hex()
|
||||
update_fixture(project_name, wasm)
|
||||
|
||||
|
||||
def process_c(project_name):
|
||||
project_path = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), f"{project_name}.c")
|
||||
)
|
||||
wasm_path = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), f"{project_name}.wasm")
|
||||
)
|
||||
build_cmd = (
|
||||
f"$CC --sysroot=$SYSROOT "
|
||||
f"-O3 -ffast-math --target=wasm32 -fno-exceptions -fno-threadsafe-statics -fvisibility=default -Wl,--export-all -Wl,--no-entry -Wl,--allow-undefined -DNDEBUG --no-standard-libraries -fno-builtin-memset "
|
||||
f"-o {wasm_path} {project_path}"
|
||||
f"&& wasm-opt {wasm_path} {OPT} -o {wasm_path}"
|
||||
)
|
||||
try:
|
||||
subprocess.run(build_cmd, shell=True, check=True)
|
||||
print(
|
||||
f"WASM file for {project_name} has been built with WASI support using clang."
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"exec error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
with open(wasm_path, "rb") as f:
|
||||
data = f.read()
|
||||
wasm = data.hex()
|
||||
update_fixture(project_name, wasm)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 2:
|
||||
print("Usage: python copyFixtures.py [<project_name>]")
|
||||
sys.exit(1)
|
||||
if len(sys.argv) == 2:
|
||||
if os.path.isdir(os.path.join(os.path.dirname(__file__), sys.argv[1])):
|
||||
process_rust(sys.argv[1])
|
||||
else:
|
||||
process_c(sys.argv[1])
|
||||
print("Fixture has been processed.")
|
||||
else:
|
||||
dirs = [
|
||||
d
|
||||
for d in os.listdir(os.path.dirname(__file__))
|
||||
if os.path.isdir(os.path.join(os.path.dirname(__file__), d))
|
||||
]
|
||||
c_files = [f for f in os.listdir(os.path.dirname(__file__)) if f.endswith(".c")]
|
||||
for d in dirs:
|
||||
process_rust(d)
|
||||
for c in c_files:
|
||||
process_c(c[:-2])
|
||||
print("All fixtures have been processed.")
|
||||
34
src/test/app/wasm_fixtures/disableFloat.wat
Normal file
34
src/test/app/wasm_fixtures/disableFloat.wat
Normal file
@@ -0,0 +1,34 @@
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(type (;1;) (func (result i32)))
|
||||
(func (;0;) (type 0))
|
||||
(func (;1;) (type 1) (result i32)
|
||||
f32.const -2048
|
||||
f32.const 2050
|
||||
f32.sub
|
||||
drop
|
||||
i32.const 1)
|
||||
(memory (;0;) 2)
|
||||
(global (;0;) i32 (i32.const 1024))
|
||||
(global (;1;) i32 (i32.const 1024))
|
||||
(global (;2;) i32 (i32.const 2048))
|
||||
(global (;3;) i32 (i32.const 2048))
|
||||
(global (;4;) i32 (i32.const 67584))
|
||||
(global (;5;) i32 (i32.const 1024))
|
||||
(global (;6;) i32 (i32.const 67584))
|
||||
(global (;7;) i32 (i32.const 131072))
|
||||
(global (;8;) i32 (i32.const 0))
|
||||
(global (;9;) i32 (i32.const 1))
|
||||
(export "memory" (memory 0))
|
||||
(export "__wasm_call_ctors" (func 0))
|
||||
(export "finish" (func 1))
|
||||
(export "buf" (global 0))
|
||||
(export "__dso_handle" (global 1))
|
||||
(export "__data_end" (global 2))
|
||||
(export "__stack_low" (global 3))
|
||||
(export "__stack_high" (global 4))
|
||||
(export "__global_base" (global 5))
|
||||
(export "__heap_base" (global 6))
|
||||
(export "__heap_end" (global 7))
|
||||
(export "__memory_base" (global 8))
|
||||
(export "__table_base" (global 9)))
|
||||
12
src/test/app/wasm_fixtures/fib.c
Normal file
12
src/test/app/wasm_fixtures/fib.c
Normal file
@@ -0,0 +1,12 @@
|
||||
// typedef long long mint;
|
||||
typedef int mint;
|
||||
|
||||
mint
|
||||
fib(mint n)
|
||||
{
|
||||
if (!n)
|
||||
return 0;
|
||||
if (n <= 2)
|
||||
return 1;
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
1320
src/test/app/wasm_fixtures/fixtures.cpp
Normal file
1320
src/test/app/wasm_fixtures/fixtures.cpp
Normal file
File diff suppressed because it is too large
Load Diff
87
src/test/app/wasm_fixtures/fixtures.h
Normal file
87
src/test/app/wasm_fixtures/fixtures.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: consider moving these to separate files (and figure out the build)
|
||||
|
||||
#include <string>
|
||||
|
||||
extern std::string const ledgerSqnWasmHex;
|
||||
|
||||
extern std::string const allHostFunctionsWasmHex;
|
||||
|
||||
extern std::string const deepRecursionHex;
|
||||
|
||||
extern std::string const fibWasmHex;
|
||||
|
||||
extern std::string const b58WasmHex;
|
||||
|
||||
extern std::string const sha512PureWasmHex;
|
||||
|
||||
extern std::string const hfPerfTest;
|
||||
|
||||
extern std::string const allKeyletsWasmHex;
|
||||
|
||||
extern std::string const codecovTestsWasmHex;
|
||||
|
||||
extern std::string const floatTestsWasmHex;
|
||||
|
||||
extern std::string const float0Hex;
|
||||
|
||||
extern std::string const disabledFloatHex;
|
||||
|
||||
extern std::string const memoryPointerAtLimitHex;
|
||||
extern std::string const memoryPointerOverLimitHex;
|
||||
extern std::string const memoryOffsetOverLimitHex;
|
||||
extern std::string const memoryEndOfWordOverLimitHex;
|
||||
extern std::string const memoryGrow0To1PageHex;
|
||||
extern std::string const memoryGrow1To0PageHex;
|
||||
extern std::string const memoryLastByteOf8MBHex;
|
||||
extern std::string const memoryGrow1MoreThan8MBHex;
|
||||
extern std::string const memoryGrow0MoreThan8MBHex;
|
||||
extern std::string const memoryInit1MoreThan8MBHex;
|
||||
extern std::string const memoryNegativeAddressHex;
|
||||
|
||||
extern std::string const table64ElementsHex;
|
||||
extern std::string const table65ElementsHex;
|
||||
extern std::string const table2TablesHex;
|
||||
extern std::string const table0ElementsHex;
|
||||
extern std::string const tableUintMaxHex;
|
||||
|
||||
extern std::string const proposalMutableGlobalHex;
|
||||
extern std::string const proposalGcStructNewHex;
|
||||
extern std::string const proposalMultiValueHex;
|
||||
extern std::string const proposalSignExtHex;
|
||||
extern std::string const proposalFloatToIntHex;
|
||||
extern std::string const proposalBulkMemoryHex;
|
||||
extern std::string const proposalRefTypesHex;
|
||||
extern std::string const proposalTailCallHex;
|
||||
extern std::string const proposalExtendedConstHex;
|
||||
extern std::string const proposalMultiMemoryHex;
|
||||
extern std::string const proposalCustomPageSizesHex;
|
||||
extern std::string const proposalMemory64Hex;
|
||||
extern std::string const proposalWideArithmeticHex;
|
||||
|
||||
extern std::string const trapDivideBy0Hex;
|
||||
extern std::string const trapIntOverflowHex;
|
||||
extern std::string const trapUnreachableHex;
|
||||
extern std::string const trapNullCallHex;
|
||||
extern std::string const trapFuncSigMismatchHex;
|
||||
|
||||
extern std::string const wasiGetTimeHex;
|
||||
extern std::string const wasiPrintHex;
|
||||
|
||||
extern std::string const badMagicNumberHex;
|
||||
extern std::string const badVersionNumberHex;
|
||||
extern std::string const lyingHeaderHex;
|
||||
extern std::string const neverEndingNumberHex;
|
||||
extern std::string const vectorLieHex;
|
||||
extern std::string const sectionOrderingHex;
|
||||
extern std::string const ghostPayloadHex;
|
||||
extern std::string const junkAfterSectionHex;
|
||||
extern std::string const invalidSectionIdHex;
|
||||
extern std::string const localVariableBombHex;
|
||||
|
||||
extern std::string const infiniteLoopWasmHex;
|
||||
extern std::string const startLoopHex;
|
||||
|
||||
extern std::string const badAllocHex;
|
||||
extern std::string const badAlignWasmHex;
|
||||
171
src/test/app/wasm_fixtures/float_tests/Cargo.lock
generated
Normal file
171
src/test/app/wasm_fixtures/float_tests/Cargo.lock
generated
Normal file
@@ -0,0 +1,171 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float_tests"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"xrpl-wasm-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "xrpl-address-macro"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=u32-buffer#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"quote",
|
||||
"sha2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xrpl-wasm-stdlib"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=u32-buffer#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8"
|
||||
dependencies = [
|
||||
"xrpl-address-macro",
|
||||
]
|
||||
21
src/test/app/wasm_fixtures/float_tests/Cargo.toml
Normal file
21
src/test/app/wasm_fixtures/float_tests/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "float_tests"
|
||||
version = "0.0.1"
|
||||
edition = "2024"
|
||||
|
||||
# This empty workspace definition keeps this project independent of the parent workspace
|
||||
[workspace]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 's'
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
xrpl-std = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-wasm-stdlib", branch = "u32-buffer" }
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
461
src/test/app/wasm_fixtures/float_tests/src/lib.rs
Normal file
461
src/test/app/wasm_fixtures/float_tests/src/lib.rs
Normal file
@@ -0,0 +1,461 @@
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_variables)]
|
||||
#![cfg_attr(target_arch = "wasm32", no_std)]
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
extern crate std;
|
||||
|
||||
use xrpl_std::core::locator::Locator;
|
||||
use xrpl_std::core::types::opaque_float::{FLOAT_NEGATIVE_ONE, FLOAT_ONE};
|
||||
use xrpl_std::decode_hex_32;
|
||||
use xrpl_std::host::trace::DataRepr::AsHex;
|
||||
use xrpl_std::host::trace::{trace, trace_data, trace_float, trace_num, DataRepr};
|
||||
use xrpl_std::host::{
|
||||
cache_ledger_obj, float_add, float_compare, float_divide, float_from_int, float_from_uint,
|
||||
float_log, float_multiply, float_pow, float_root, float_set, float_subtract,
|
||||
get_ledger_obj_array_len, get_ledger_obj_field, get_ledger_obj_nested_field,
|
||||
trace_opaque_float, FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
};
|
||||
use xrpl_std::sfield;
|
||||
use xrpl_std::sfield::{
|
||||
Account, AccountTxnID, Balance, Domain, EmailHash, Flags, LedgerEntryType, MessageKey,
|
||||
OwnerCount, PreviousTxnID, PreviousTxnLgrSeq, RegularKey, Sequence, TicketCount, TransferRate,
|
||||
};
|
||||
|
||||
fn test_float_from_wasm() {
|
||||
let _ = trace("\n$$$ test_float_from_wasm $$$");
|
||||
|
||||
let mut f: [u8; 8] = [0u8; 8];
|
||||
if 8 == unsafe { float_from_int(12300, f.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) } {
|
||||
let _ = trace_float(" float from i64 12300:", &f);
|
||||
let _ = trace_data(" float from i64 12300 as HEX:", &f, AsHex);
|
||||
} else {
|
||||
let _ = trace(" float from i64 12300: failed");
|
||||
}
|
||||
|
||||
let u64_value: u64 = 12300;
|
||||
if 8 == unsafe {
|
||||
float_from_uint(
|
||||
&u64_value as *const u64 as *const u8,
|
||||
8,
|
||||
f.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
} {
|
||||
let _ = trace_float(" float from u64 12300:", &f);
|
||||
} else {
|
||||
let _ = trace(" float from u64 12300: failed");
|
||||
}
|
||||
|
||||
if 8 == unsafe { float_set(2, 123, f.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) } {
|
||||
let _ = trace_float(" float from exp 2, mantissa 123:", &f);
|
||||
} else {
|
||||
let _ = trace(" float from exp 2, mantissa 3: failed");
|
||||
}
|
||||
|
||||
let _ = trace_float(" float from const 1:", &FLOAT_ONE);
|
||||
let _ = trace_float(" float from const -1:", &FLOAT_NEGATIVE_ONE);
|
||||
}
|
||||
|
||||
fn test_float_compare() {
|
||||
let _ = trace("\n$$$ test_float_compare $$$");
|
||||
|
||||
let mut f1: [u8; 8] = [0u8; 8];
|
||||
if 8 != unsafe { float_from_int(1, f1.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) } {
|
||||
let _ = trace(" float from 1: failed");
|
||||
} else {
|
||||
let _ = trace_float(" float from 1:", &f1);
|
||||
}
|
||||
|
||||
if 0 == unsafe { float_compare(f1.as_ptr(), 8, FLOAT_ONE.as_ptr(), 8) } {
|
||||
let _ = trace(" float from 1 == FLOAT_ONE");
|
||||
} else {
|
||||
let _ = trace(" float from 1 != FLOAT_ONE");
|
||||
}
|
||||
|
||||
if 1 == unsafe { float_compare(f1.as_ptr(), 8, FLOAT_NEGATIVE_ONE.as_ptr(), 8) } {
|
||||
let _ = trace(" float from 1 > FLOAT_NEGATIVE_ONE");
|
||||
} else {
|
||||
let _ = trace(" float from 1 !> FLOAT_NEGATIVE_ONE");
|
||||
}
|
||||
|
||||
if 2 == unsafe { float_compare(FLOAT_NEGATIVE_ONE.as_ptr(), 8, f1.as_ptr(), 8) } {
|
||||
let _ = trace(" FLOAT_NEGATIVE_ONE < float from 1");
|
||||
} else {
|
||||
let _ = trace(" FLOAT_NEGATIVE_ONE !< float from 1");
|
||||
}
|
||||
}
|
||||
|
||||
fn test_float_add_subtract() {
|
||||
let _ = trace("\n$$$ test_float_add_subtract $$$");
|
||||
|
||||
let mut f_compute: [u8; 8] = FLOAT_ONE;
|
||||
for i in 0..9 {
|
||||
unsafe {
|
||||
float_add(
|
||||
f_compute.as_ptr(),
|
||||
8,
|
||||
FLOAT_ONE.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
// let _ = trace_float(" float:", &f_compute);
|
||||
}
|
||||
let mut f10: [u8; 8] = [0u8; 8];
|
||||
if 8 != unsafe { float_from_int(10, f10.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) } {
|
||||
// let _ = trace(" float from 10: failed");
|
||||
}
|
||||
if 0 == unsafe { float_compare(f10.as_ptr(), 8, f_compute.as_ptr(), 8) } {
|
||||
let _ = trace(" repeated add: good");
|
||||
} else {
|
||||
let _ = trace(" repeated add: bad");
|
||||
}
|
||||
|
||||
for i in 0..11 {
|
||||
unsafe {
|
||||
float_subtract(
|
||||
f_compute.as_ptr(),
|
||||
8,
|
||||
FLOAT_ONE.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
}
|
||||
if 0 == unsafe { float_compare(f_compute.as_ptr(), 8, FLOAT_NEGATIVE_ONE.as_ptr(), 8) } {
|
||||
let _ = trace(" repeated subtract: good");
|
||||
} else {
|
||||
let _ = trace(" repeated subtract: bad");
|
||||
}
|
||||
}
|
||||
|
||||
fn test_float_multiply_divide() {
|
||||
let _ = trace("\n$$$ test_float_multiply_divide $$$");
|
||||
|
||||
let mut f10: [u8; 8] = [0u8; 8];
|
||||
unsafe { float_from_int(10, f10.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) };
|
||||
let mut f_compute: [u8; 8] = FLOAT_ONE;
|
||||
for i in 0..6 {
|
||||
unsafe {
|
||||
float_multiply(
|
||||
f_compute.as_ptr(),
|
||||
8,
|
||||
f10.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
// let _ = trace_float(" float:", &f_compute);
|
||||
}
|
||||
let mut f1000000: [u8; 8] = [0u8; 8];
|
||||
unsafe {
|
||||
float_from_int(
|
||||
1000000,
|
||||
f1000000.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
|
||||
if 0 == unsafe { float_compare(f1000000.as_ptr(), 8, f_compute.as_ptr(), 8) } {
|
||||
let _ = trace(" repeated multiply: good");
|
||||
} else {
|
||||
let _ = trace(" repeated multiply: bad");
|
||||
}
|
||||
|
||||
for i in 0..7 {
|
||||
unsafe {
|
||||
float_divide(
|
||||
f_compute.as_ptr(),
|
||||
8,
|
||||
f10.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
}
|
||||
let mut f01: [u8; 8] = [0u8; 8];
|
||||
unsafe { float_set(-1, 1, f01.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) };
|
||||
|
||||
if 0 == unsafe { float_compare(f_compute.as_ptr(), 8, f01.as_ptr(), 8) } {
|
||||
let _ = trace(" repeated divide: good");
|
||||
} else {
|
||||
let _ = trace(" repeated divide: bad");
|
||||
}
|
||||
}
|
||||
|
||||
fn test_float_pow() {
|
||||
let _ = trace("\n$$$ test_float_pow $$$");
|
||||
|
||||
let mut f_compute: [u8; 8] = [0u8; 8];
|
||||
unsafe {
|
||||
float_pow(
|
||||
FLOAT_ONE.as_ptr(),
|
||||
8,
|
||||
3,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float cube of 1:", &f_compute);
|
||||
|
||||
unsafe {
|
||||
float_pow(
|
||||
FLOAT_NEGATIVE_ONE.as_ptr(),
|
||||
8,
|
||||
6,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float 6th power of -1:", &f_compute);
|
||||
|
||||
let mut f9: [u8; 8] = [0u8; 8];
|
||||
unsafe { float_from_int(9, f9.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) };
|
||||
unsafe {
|
||||
float_pow(
|
||||
f9.as_ptr(),
|
||||
8,
|
||||
2,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float square of 9:", &f_compute);
|
||||
|
||||
unsafe {
|
||||
float_pow(
|
||||
f9.as_ptr(),
|
||||
8,
|
||||
0,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float 0th power of 9:", &f_compute);
|
||||
|
||||
let mut f0: [u8; 8] = [0u8; 8];
|
||||
unsafe { float_from_int(0, f0.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) };
|
||||
unsafe {
|
||||
float_pow(
|
||||
f0.as_ptr(),
|
||||
8,
|
||||
2,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float square of 0:", &f_compute);
|
||||
|
||||
let r = unsafe {
|
||||
float_pow(
|
||||
f0.as_ptr(),
|
||||
8,
|
||||
0,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_num(
|
||||
" float 0th power of 0 (expecting INVALID_PARAMS error):",
|
||||
r as i64,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_float_root() {
|
||||
let _ = trace("\n$$$ test_float_root $$$");
|
||||
|
||||
let mut f9: [u8; 8] = [0u8; 8];
|
||||
unsafe { float_from_int(9, f9.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) };
|
||||
let mut f_compute: [u8; 8] = [0u8; 8];
|
||||
unsafe {
|
||||
float_root(
|
||||
f9.as_ptr(),
|
||||
8,
|
||||
2,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float sqrt of 9:", &f_compute);
|
||||
unsafe {
|
||||
float_root(
|
||||
f9.as_ptr(),
|
||||
8,
|
||||
3,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float cbrt of 9:", &f_compute);
|
||||
|
||||
let mut f1000000: [u8; 8] = [0u8; 8];
|
||||
unsafe {
|
||||
float_from_int(
|
||||
1000000,
|
||||
f1000000.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
float_root(
|
||||
f1000000.as_ptr(),
|
||||
8,
|
||||
3,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float cbrt of 1000000:", &f_compute);
|
||||
unsafe {
|
||||
float_root(
|
||||
f1000000.as_ptr(),
|
||||
8,
|
||||
6,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" float 6th root of 1000000:", &f_compute);
|
||||
}
|
||||
|
||||
fn test_float_log() {
|
||||
let _ = trace("\n$$$ test_float_log $$$");
|
||||
|
||||
let mut f1000000: [u8; 8] = [0u8; 8];
|
||||
unsafe {
|
||||
float_from_int(
|
||||
1000000,
|
||||
f1000000.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let mut f_compute: [u8; 8] = [0u8; 8];
|
||||
unsafe {
|
||||
float_log(
|
||||
f1000000.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" log_10 of 1000000:", &f_compute);
|
||||
}
|
||||
|
||||
fn test_float_negate() {
|
||||
let _ = trace("\n$$$ test_float_negate $$$");
|
||||
|
||||
let mut f_compute: [u8; 8] = [0u8; 8];
|
||||
unsafe {
|
||||
float_multiply(
|
||||
FLOAT_ONE.as_ptr(),
|
||||
8,
|
||||
FLOAT_NEGATIVE_ONE.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
// let _ = trace_float(" float:", &f_compute);
|
||||
if 0 == unsafe { float_compare(FLOAT_NEGATIVE_ONE.as_ptr(), 8, f_compute.as_ptr(), 8) } {
|
||||
let _ = trace(" negate const 1: good");
|
||||
} else {
|
||||
let _ = trace(" negate const 1: bad");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
float_multiply(
|
||||
FLOAT_NEGATIVE_ONE.as_ptr(),
|
||||
8,
|
||||
FLOAT_NEGATIVE_ONE.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
// let _ = trace_float(" float:", &f_compute);
|
||||
if 0 == unsafe { float_compare(FLOAT_ONE.as_ptr(), 8, f_compute.as_ptr(), 8) } {
|
||||
let _ = trace(" negate const -1: good");
|
||||
} else {
|
||||
let _ = trace(" negate const -1: bad");
|
||||
}
|
||||
}
|
||||
|
||||
fn test_float_invert() {
|
||||
let _ = trace("\n$$$ test_float_invert $$$");
|
||||
|
||||
let mut f_compute: [u8; 8] = [0u8; 8];
|
||||
let mut f10: [u8; 8] = [0u8; 8];
|
||||
unsafe { float_from_int(10, f10.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) };
|
||||
unsafe {
|
||||
float_divide(
|
||||
FLOAT_ONE.as_ptr(),
|
||||
8,
|
||||
f10.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" invert a float from 10:", &f_compute);
|
||||
unsafe {
|
||||
float_divide(
|
||||
FLOAT_ONE.as_ptr(),
|
||||
8,
|
||||
f_compute.as_ptr(),
|
||||
8,
|
||||
f_compute.as_mut_ptr(),
|
||||
8,
|
||||
FLOAT_ROUNDING_MODES_TO_NEAREST,
|
||||
)
|
||||
};
|
||||
let _ = trace_float(" invert again:", &f_compute);
|
||||
|
||||
// if f10's value is 7, then invert twice won't match the original value
|
||||
if 0 == unsafe { float_compare(f10.as_ptr(), 8, f_compute.as_ptr(), 8) } {
|
||||
let _ = trace(" invert twice: good");
|
||||
} else {
|
||||
let _ = trace(" invert twice: bad");
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn finish() -> i32 {
|
||||
test_float_from_wasm();
|
||||
test_float_compare();
|
||||
test_float_add_subtract();
|
||||
test_float_multiply_divide();
|
||||
test_float_pow();
|
||||
test_float_root();
|
||||
test_float_log();
|
||||
test_float_negate();
|
||||
test_float_invert();
|
||||
|
||||
1
|
||||
}
|
||||
8
src/test/app/wasm_fixtures/infiniteLoop.c
Normal file
8
src/test/app/wasm_fixtures/infiniteLoop.c
Normal file
@@ -0,0 +1,8 @@
|
||||
int
|
||||
loop()
|
||||
{
|
||||
int volatile x = 0;
|
||||
while (1)
|
||||
x++;
|
||||
return x;
|
||||
}
|
||||
16
src/test/app/wasm_fixtures/ledgerSqn.c
Normal file
16
src/test/app/wasm_fixtures/ledgerSqn.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <stdint.h>
|
||||
|
||||
int32_t
|
||||
get_ledger_sqn(uint8_t*, int32_t);
|
||||
|
||||
int
|
||||
finish()
|
||||
{
|
||||
uint32_t sqn;
|
||||
int32_t result = get_ledger_sqn((uint8_t*)&sqn, sizeof(sqn));
|
||||
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return sqn >= 5 ? 5 : 0;
|
||||
}
|
||||
133
src/test/app/wasm_fixtures/sha512Pure.c
Normal file
133
src/test/app/wasm_fixtures/sha512Pure.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static uint64_t const K512[] = {
|
||||
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
|
||||
0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
|
||||
0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
|
||||
0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
|
||||
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
|
||||
0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
|
||||
0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
|
||||
0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
|
||||
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
|
||||
0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
|
||||
0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
|
||||
0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
|
||||
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
|
||||
0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
|
||||
0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
|
||||
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817};
|
||||
|
||||
#define ROTATE(x, y) (((x) >> (y)) | ((x) << (64 - (y))))
|
||||
#define Sigma0(x) (ROTATE((x), 28) ^ ROTATE((x), 34) ^ ROTATE((x), 39))
|
||||
#define Sigma1(x) (ROTATE((x), 14) ^ ROTATE((x), 18) ^ ROTATE((x), 41))
|
||||
#define sigma0(x) (ROTATE((x), 1) ^ ROTATE((x), 8) ^ ((x) >> 7))
|
||||
#define sigma1(x) (ROTATE((x), 19) ^ ROTATE((x), 61) ^ ((x) >> 6))
|
||||
|
||||
#define Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
|
||||
#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||
|
||||
static inline uint64_t
|
||||
B2U64(uint8_t val, uint8_t sh)
|
||||
{
|
||||
return ((uint64_t)val) << sh;
|
||||
}
|
||||
|
||||
void*
|
||||
allocate(int sz)
|
||||
{
|
||||
return malloc(sz);
|
||||
}
|
||||
void
|
||||
deallocate(void* p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
uint8_t e_data[32 * 1024];
|
||||
|
||||
uint8_t*
|
||||
sha512_process(uint8_t const* data, int32_t length)
|
||||
{
|
||||
static uint64_t state[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
uint64_t a, b, c, d, e, f, g, h, s0, s1, T1, T2;
|
||||
uint64_t X[16];
|
||||
|
||||
uint64_t blocks = length / 128;
|
||||
while (blocks--)
|
||||
{
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
f = state[5];
|
||||
g = state[6];
|
||||
h = state[7];
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
X[i] = B2U64(data[0], 56) | B2U64(data[1], 48) | B2U64(data[2], 40) | B2U64(data[3], 32) |
|
||||
B2U64(data[4], 24) | B2U64(data[5], 16) | B2U64(data[6], 8) | B2U64(data[7], 0);
|
||||
data += 8;
|
||||
|
||||
T1 = h;
|
||||
T1 += Sigma1(e);
|
||||
T1 += Ch(e, f, g);
|
||||
T1 += K512[i];
|
||||
T1 += X[i];
|
||||
|
||||
T2 = Sigma0(a);
|
||||
T2 += Maj(a, b, c);
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + T1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = T1 + T2;
|
||||
}
|
||||
|
||||
for (i = 16; i < 80; i++)
|
||||
{
|
||||
s0 = X[(i + 1) & 0x0f];
|
||||
s0 = sigma0(s0);
|
||||
s1 = X[(i + 14) & 0x0f];
|
||||
s1 = sigma1(s1);
|
||||
|
||||
T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf];
|
||||
T1 += h + Sigma1(e) + Ch(e, f, g) + K512[i];
|
||||
T2 = Sigma0(a) + Maj(a, b, c);
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + T1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = T1 + T2;
|
||||
}
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
state[5] += f;
|
||||
state[6] += g;
|
||||
state[7] += h;
|
||||
}
|
||||
|
||||
return (uint8_t*)(state);
|
||||
}
|
||||
|
||||
// int main ()
|
||||
//{
|
||||
// return 0;
|
||||
// }
|
||||
13
src/test/app/wasm_fixtures/wat/custom_page_sizes.wat
Normal file
13
src/test/app/wasm_fixtures/wat/custom_page_sizes.wat
Normal file
@@ -0,0 +1,13 @@
|
||||
(module
|
||||
;; Define a memory with 1 initial page.
|
||||
;; CRITICAL: We explicitly set the page size to 1 kilobyte.
|
||||
;; Standard Wasm implies (pagesize 65536).
|
||||
(memory 1 (pagesize 1024))
|
||||
|
||||
(func $finish (result i32)
|
||||
;; If this module instantiates, the runtime accepted the custom page size.
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
29
src/test/app/wasm_fixtures/wat/deep_recursion.wat
Normal file
29
src/test/app/wasm_fixtures/wat/deep_recursion.wat
Normal file
@@ -0,0 +1,29 @@
|
||||
(module
|
||||
;; Define a Mutable Global Variable to act as our counter.
|
||||
;; We initialize it to 1,000,000.
|
||||
(global $counter (mut i32) (i32.const 1000000))
|
||||
|
||||
(func $finish (result i32)
|
||||
;; 1. Check if counter == 0 (Base Case)
|
||||
global.get $counter
|
||||
i32.eqz
|
||||
if
|
||||
;; If counter is 0, we are done. Return 1.
|
||||
i32.const 1
|
||||
return
|
||||
end
|
||||
|
||||
;; 2. Decrement the Global Counter
|
||||
global.get $counter
|
||||
i32.const 1
|
||||
i32.sub
|
||||
global.set $counter
|
||||
|
||||
;; 3. Recursive Step: Call SELF
|
||||
;; This puts an i32 (1) on the stack when it returns.
|
||||
call $finish
|
||||
)
|
||||
|
||||
;; Export the only function we have
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
21
src/test/app/wasm_fixtures/wat/memory64.wat
Normal file
21
src/test/app/wasm_fixtures/wat/memory64.wat
Normal file
@@ -0,0 +1,21 @@
|
||||
(module
|
||||
;; Define a 64-bit memory (index type i64)
|
||||
;; Start with 1 page.
|
||||
(memory i64 1)
|
||||
|
||||
(func $finish (result i32)
|
||||
;; 1. Perform a store using a 64-bit address.
|
||||
;; Even if the value is small (0), the type MUST be i64.
|
||||
i64.const 0 ;; Address (64-bit)
|
||||
i32.const 42 ;; Value (32-bit)
|
||||
i32.store8 ;; Opcode doesn't change, but validation rules do.
|
||||
|
||||
;; 2. check memory size
|
||||
;; memory.size now returns an i64.
|
||||
memory.size
|
||||
i64.const 1
|
||||
i64.eq ;; Returns i32 (1 if true)
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
(module
|
||||
;; 1. Define Memory: 1 Page = 64KB = 65,536 bytes
|
||||
(memory 1)
|
||||
|
||||
;; Export memory so the host can inspect it if needed
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $test_straddle (result i32)
|
||||
;; Push the address onto the stack.
|
||||
;; 65534 is valid, but it is only 2 bytes away from the end.
|
||||
i32.const 65534
|
||||
|
||||
;; Attempt to load an i32 (4 bytes) from that address.
|
||||
;; This requires bytes 65534, 65535, 65536, and 65537.
|
||||
;; Since 65536 is the first invalid byte, this MUST trap.
|
||||
i32.load
|
||||
|
||||
;; Clean up the stack.
|
||||
;; The load pushed a value, but we don't care what it is.
|
||||
drop
|
||||
|
||||
;; Return 1 to signal "I survived the memory access"
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
;; Export the function so you can call it from your host (JS, Python, etc.)
|
||||
(export "finish" (func $test_straddle))
|
||||
)
|
||||
@@ -0,0 +1,29 @@
|
||||
(module
|
||||
;; Start at your limit: 128 pages (8MB)
|
||||
(memory 128)
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $try_grow_beyond_limit (result i32)
|
||||
;; Attempt to grow by 0 page
|
||||
i32.const 0
|
||||
memory.grow
|
||||
|
||||
;; memory.grow returns:
|
||||
;; -1 if the growth failed (Correct behavior for your limit)
|
||||
;; 128 (old size) if growth succeeded (Means limit was bypassed)
|
||||
|
||||
;; Check if result == -1
|
||||
i32.const -1
|
||||
i32.eq
|
||||
if
|
||||
;; Growth FAILED (Host blocked it). Return -1.
|
||||
i32.const -1
|
||||
return
|
||||
end
|
||||
|
||||
;; Growth SUCCEEDED (Host allowed it). Return 1.
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $try_grow_beyond_limit))
|
||||
)
|
||||
26
src/test/app/wasm_fixtures/wat/memory_grow_0_to_1.wat
Normal file
26
src/test/app/wasm_fixtures/wat/memory_grow_0_to_1.wat
Normal file
@@ -0,0 +1,26 @@
|
||||
(module
|
||||
;; 1. Define Memory: Start with 0 pages
|
||||
(memory 0)
|
||||
|
||||
;; Export memory to host
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $grow_from_zero (result i32)
|
||||
;; We have 0 pages. We want to add 1 page.
|
||||
;; Push delta (1) onto stack.
|
||||
i32.const 1
|
||||
|
||||
;; Grow the memory.
|
||||
;; If successful: memory becomes 64KB, returns old size (0).
|
||||
;; If failed: memory stays 0, returns -1.
|
||||
memory.grow
|
||||
|
||||
;; Drop the return value of memory.grow
|
||||
drop
|
||||
|
||||
;; Return 1 (as requested)
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $grow_from_zero))
|
||||
)
|
||||
@@ -0,0 +1,29 @@
|
||||
(module
|
||||
;; Start at your limit: 128 pages (8MB)
|
||||
(memory 128)
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $try_grow_beyond_limit (result i32)
|
||||
;; Attempt to grow by 1 page
|
||||
i32.const 1
|
||||
memory.grow
|
||||
|
||||
;; memory.grow returns:
|
||||
;; -1 if the growth failed (Correct behavior for your limit)
|
||||
;; 128 (old size) if growth succeeded (Means limit was bypassed)
|
||||
|
||||
;; Check if result == -1
|
||||
i32.const -1
|
||||
i32.eq
|
||||
if
|
||||
;; Growth FAILED (Host blocked it). Return -1.
|
||||
i32.const -1
|
||||
return
|
||||
end
|
||||
|
||||
;; Growth SUCCEEDED (Host allowed it). Return 1.
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $try_grow_beyond_limit))
|
||||
)
|
||||
33
src/test/app/wasm_fixtures/wat/memory_grow_1_to_0.wat
Normal file
33
src/test/app/wasm_fixtures/wat/memory_grow_1_to_0.wat
Normal file
@@ -0,0 +1,33 @@
|
||||
(module
|
||||
;; 1. Define Memory: Start with 1 page (64KB)
|
||||
(memory 1)
|
||||
|
||||
;; Export memory to host
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $grow_negative (result i32)
|
||||
;; The user pushed -1. In Wasm, this is interpreted as unsigned MAX_UINT32.
|
||||
;; This is requesting to add 4,294,967,295 pages (approx 256 TB).
|
||||
;; A secure runtime MUST fail this request (return -1) without crashing.
|
||||
i32.const -1
|
||||
|
||||
;; Grow the memory.
|
||||
;; Returns: old_size if success, -1 if failure.
|
||||
memory.grow
|
||||
|
||||
;; Check if result == -1 (Failure)
|
||||
i32.const -1
|
||||
i32.eq
|
||||
if
|
||||
;; If memory.grow returned -1, we return -1 to signal "Correctly failed".
|
||||
i32.const -1
|
||||
return
|
||||
end
|
||||
|
||||
;; If we are here, memory.grow somehow SUCCEEDED (Vulnerability).
|
||||
;; We return 1 to signal "Unexpected Success".
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $grow_negative))
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
(module
|
||||
;; Define memory: 129 pages (> 8MB limit) min, 129 pages max
|
||||
(memory 129 129)
|
||||
|
||||
;; Export memory so host can verify size
|
||||
(export "memory" (memory 0))
|
||||
|
||||
;; access last byte of 8MB limit
|
||||
(func $access_last_byte (result i32)
|
||||
;; Math: 128 pages * 64,536 bytes/page = 8,388,608 bytes
|
||||
;; Valid indices: 0 to 8,388,607
|
||||
|
||||
;; Push the address of the LAST valid byte
|
||||
i32.const 8388607
|
||||
|
||||
;; Load byte from that address
|
||||
i32.load8_u
|
||||
|
||||
;; Drop the value (we don't care what it is, just that we could read it)
|
||||
drop
|
||||
|
||||
;; Return 1 to indicate success
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $access_last_byte))
|
||||
)
|
||||
26
src/test/app/wasm_fixtures/wat/memory_last_byte_of_8MB.wat
Normal file
26
src/test/app/wasm_fixtures/wat/memory_last_byte_of_8MB.wat
Normal file
@@ -0,0 +1,26 @@
|
||||
(module
|
||||
;; Define memory: 128 pages (8MB) min, 128 pages max
|
||||
(memory 128 128)
|
||||
|
||||
;; Export memory so host can verify size
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $access_last_byte (result i32)
|
||||
;; Math: 128 pages * 64,536 bytes/page = 8,388,608 bytes
|
||||
;; Valid indices: 0 to 8,388,607
|
||||
|
||||
;; Push the address of the LAST valid byte
|
||||
i32.const 8388607
|
||||
|
||||
;; Load byte from that address
|
||||
i32.load8_u
|
||||
|
||||
;; Drop the value (we don't care what it is, just that we could read it)
|
||||
drop
|
||||
|
||||
;; Return 1 to indicate success
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $access_last_byte))
|
||||
)
|
||||
23
src/test/app/wasm_fixtures/wat/memory_negative_address.wat
Normal file
23
src/test/app/wasm_fixtures/wat/memory_negative_address.wat
Normal file
@@ -0,0 +1,23 @@
|
||||
(module
|
||||
;; Define memory: 128 pages (8MB) min, 128 pages max
|
||||
(memory 128 128)
|
||||
|
||||
;; Export memory so host can verify size
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $access_last_byte (result i32)
|
||||
;; Push a negative address
|
||||
i32.const -1
|
||||
|
||||
;; Load byte from that address
|
||||
i32.load8_u
|
||||
|
||||
;; Drop the value
|
||||
drop
|
||||
|
||||
;; Return 1 to indicate success
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $access_last_byte))
|
||||
)
|
||||
27
src/test/app/wasm_fixtures/wat/memory_offset_over_limit.wat
Normal file
27
src/test/app/wasm_fixtures/wat/memory_offset_over_limit.wat
Normal file
@@ -0,0 +1,27 @@
|
||||
(module
|
||||
;; 1. Define Memory: 1 Page = 64KB
|
||||
(memory 1)
|
||||
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $test_offset_overflow (result i32)
|
||||
;; 1. Push the base address onto the stack.
|
||||
;; We use '0', which is the safest, most valid address possible.
|
||||
i32.const 0
|
||||
|
||||
;; 2. Attempt to load using a static offset.
|
||||
;; syntax: i32.load offset=N align=N
|
||||
;; We set the offset to 65536 (the size of the memory).
|
||||
;; The effective address becomes 0 + 65536 = 65536.
|
||||
i32.load offset=65536
|
||||
|
||||
;; Clean up the stack.
|
||||
;; The load pushed a value, but we don't care what it is.
|
||||
drop
|
||||
|
||||
;; Return 1 to signal "I survived the memory access"
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $test_offset_overflow))
|
||||
)
|
||||
22
src/test/app/wasm_fixtures/wat/memory_pointer_at_limit.wat
Normal file
22
src/test/app/wasm_fixtures/wat/memory_pointer_at_limit.wat
Normal file
@@ -0,0 +1,22 @@
|
||||
(module
|
||||
;; Define 1 page of memory (64KB = 65,536 bytes)
|
||||
(memory 1)
|
||||
|
||||
(func $read_edge (result i32)
|
||||
;; Push the index of the LAST valid byte
|
||||
i32.const 65535
|
||||
|
||||
;; Load 1 byte (unsigned)
|
||||
i32.load8_u
|
||||
|
||||
;; Clean up the stack.
|
||||
;; The load pushed a value, but we don't care what it is.
|
||||
drop
|
||||
|
||||
;; Return 1 to signal "I survived the memory access"
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
;; Export as "finish" as requested
|
||||
(export "finish" (func $read_edge))
|
||||
)
|
||||
23
src/test/app/wasm_fixtures/wat/memory_pointer_over_limit.wat
Normal file
23
src/test/app/wasm_fixtures/wat/memory_pointer_over_limit.wat
Normal file
@@ -0,0 +1,23 @@
|
||||
(module
|
||||
;; Define 1 page of memory (64KB = 65,536 bytes)
|
||||
(memory 1)
|
||||
|
||||
(func $read_overflow (result i32)
|
||||
;; Push the index of the FIRST invalid byte
|
||||
;; Memory is 0..65535, so 65536 is out of bounds.
|
||||
i32.const 65536
|
||||
|
||||
;; Load 1 byte (unsigned)
|
||||
i32.load8_u
|
||||
|
||||
;; Clean up the stack.
|
||||
;; The load pushed a value, but we don't care what it is.
|
||||
drop
|
||||
|
||||
;; Return 1 to signal "I survived the memory access"
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
;; Export as "finish" as requested
|
||||
(export "finish" (func $read_overflow))
|
||||
)
|
||||
16
src/test/app/wasm_fixtures/wat/multi_memory.wat
Normal file
16
src/test/app/wasm_fixtures/wat/multi_memory.wat
Normal file
@@ -0,0 +1,16 @@
|
||||
(module
|
||||
;; Memory 0: Index 0 (Empty)
|
||||
(memory 0)
|
||||
|
||||
;; Memory 1: Index 1 (Size 1 page)
|
||||
;; If multi-memory is disabled, this line causes a validation error (max 1 memory).
|
||||
(memory 1)
|
||||
|
||||
(func $finish (result i32)
|
||||
;; Query size of Memory Index 1.
|
||||
;; Should return 1 (success).
|
||||
memory.size 1
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
25
src/test/app/wasm_fixtures/wat/proposal_bulk_memory.wat
Normal file
25
src/test/app/wasm_fixtures/wat/proposal_bulk_memory.wat
Normal file
@@ -0,0 +1,25 @@
|
||||
(module
|
||||
;; Define 1 page of memory
|
||||
(memory 1)
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $test_bulk_ops (result i32)
|
||||
;; Setup: Write value 42 at index 0 so we have something to copy
|
||||
(i32.store8 (i32.const 0) (i32.const 42))
|
||||
|
||||
;; Test memory.copy (Opcode 0xFC 0x0A)
|
||||
;; Copy 1 byte from offset 0 to offset 100
|
||||
(memory.copy
|
||||
(i32.const 100) ;; Destination Offset
|
||||
(i32.const 0) ;; Source Offset
|
||||
(i32.const 1) ;; Size (bytes)
|
||||
)
|
||||
|
||||
;; Verify: Read byte at offset 100. Should be 42.
|
||||
(i32.load8_u (i32.const 100))
|
||||
(i32.const 42)
|
||||
i32.eq
|
||||
)
|
||||
|
||||
(export "finish" (func $test_bulk_ops))
|
||||
)
|
||||
15
src/test/app/wasm_fixtures/wat/proposal_extended_const.wat
Normal file
15
src/test/app/wasm_fixtures/wat/proposal_extended_const.wat
Normal file
@@ -0,0 +1,15 @@
|
||||
(module
|
||||
;; 1. Define a global using an EXTENDED constant expression.
|
||||
;; MVP only allows (i32.const X).
|
||||
;; This proposal allows (i32.add (i32.const X) (i32.const Y)).
|
||||
(global $g i32 (i32.add (i32.const 10) (i32.const 32)))
|
||||
|
||||
(func $finish (result i32)
|
||||
;; 2. verify the global equals 42
|
||||
global.get $g
|
||||
i32.const 42
|
||||
i32.eq
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
18
src/test/app/wasm_fixtures/wat/proposal_float_to_int.wat
Normal file
18
src/test/app/wasm_fixtures/wat/proposal_float_to_int.wat
Normal file
@@ -0,0 +1,18 @@
|
||||
(module
|
||||
(func $test_saturation (result i32)
|
||||
;; 1. Push a float that is too big for a 32-bit integer
|
||||
;; 1e10 (10 billion) > 2.14 billion (Max i32)
|
||||
f32.const 1.0e10
|
||||
|
||||
;; 2. Attempt saturating conversion (Opcode 0xFC 0x00)
|
||||
;; If supported: Clamps to MAX_I32.
|
||||
;; If disabled: Validation error (unknown instruction).
|
||||
i32.trunc_sat_f32_s
|
||||
|
||||
;; 3. Check if result is MAX_I32 (2147483647)
|
||||
i32.const 2147483647
|
||||
i32.eq
|
||||
)
|
||||
|
||||
(export "finish" (func $test_saturation))
|
||||
)
|
||||
12
src/test/app/wasm_fixtures/wat/proposal_gc_struct_new.wat
Normal file
12
src/test/app/wasm_fixtures/wat/proposal_gc_struct_new.wat
Normal file
@@ -0,0 +1,12 @@
|
||||
;; generated by wasm-tools print gc_test.wasm that has the following hex
|
||||
;; 0061736d01000000010b026000017f5f027f017f0103020100070a010666696e69736800000a0a010800fb01011a41010b
|
||||
(module
|
||||
(type (;0;) (func (result i32)))
|
||||
(type (;1;) (struct (field (mut i32)) (field (mut i32))))
|
||||
(export "finish" (func 0))
|
||||
(func (;0;) (type 0) (result i32)
|
||||
struct.new_default 1
|
||||
drop
|
||||
i32.const 1
|
||||
)
|
||||
)
|
||||
22
src/test/app/wasm_fixtures/wat/proposal_multi_value.wat
Normal file
22
src/test/app/wasm_fixtures/wat/proposal_multi_value.wat
Normal file
@@ -0,0 +1,22 @@
|
||||
(module
|
||||
;; 1. Function returning TWO values (Multi-Value feature)
|
||||
(func $get_numbers (result i32 i32)
|
||||
i32.const 10
|
||||
i32.const 20
|
||||
)
|
||||
|
||||
(func $finish (result i32)
|
||||
;; Call pushes [10, 20] onto the stack
|
||||
call $get_numbers
|
||||
|
||||
;; 2. Block taking TWO parameters (Multi-Value feature)
|
||||
;; It consumes the [10, 20] from the stack.
|
||||
block (param i32 i32) (result i32)
|
||||
i32.add ;; 10 + 20 = 30
|
||||
i32.const 30 ;; Expected result
|
||||
i32.eq ;; Compare: returns 1 if equal
|
||||
end
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
25
src/test/app/wasm_fixtures/wat/proposal_mutable_global.wat
Normal file
25
src/test/app/wasm_fixtures/wat/proposal_mutable_global.wat
Normal file
@@ -0,0 +1,25 @@
|
||||
(module
|
||||
;; Define a mutable global initialized to 0
|
||||
(global $counter (mut i32) (i32.const 0))
|
||||
|
||||
;; EXPORTING a mutable global is the key feature of this proposal.
|
||||
;; In strict MVP, exported globals had to be immutable (const).
|
||||
(export "counter" (global $counter))
|
||||
|
||||
(func $finish (result i32)
|
||||
;; 1. Get current value
|
||||
global.get $counter
|
||||
|
||||
;; 2. Add 1
|
||||
i32.const 1
|
||||
i32.add
|
||||
|
||||
;; 3. Set new value (Mutation)
|
||||
global.set $counter
|
||||
|
||||
;; 4. Return 1 for success
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
18
src/test/app/wasm_fixtures/wat/proposal_ref_types.wat
Normal file
18
src/test/app/wasm_fixtures/wat/proposal_ref_types.wat
Normal file
@@ -0,0 +1,18 @@
|
||||
(module
|
||||
;; Import a table from the host that holds externrefs
|
||||
(import "env" "table" (table 1 externref))
|
||||
|
||||
(func $test_ref_types (result i32)
|
||||
;; Store a null externref into the table at index 0
|
||||
;; If reference_types is disabled, 'externref' and 'ref.null' will fail parsing.
|
||||
(table.set
|
||||
(i32.const 0) ;; Index
|
||||
(ref.null extern) ;; Value (Null External Reference)
|
||||
)
|
||||
|
||||
;; Return 1 (Success)
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $test_ref_types))
|
||||
)
|
||||
18
src/test/app/wasm_fixtures/wat/proposal_sign_ext.wat
Normal file
18
src/test/app/wasm_fixtures/wat/proposal_sign_ext.wat
Normal file
@@ -0,0 +1,18 @@
|
||||
(module
|
||||
(func $test_sign_ext (result i32)
|
||||
;; Push 255 (0x000000FF) onto the stack
|
||||
i32.const 255
|
||||
|
||||
;; Sign-extend from 8-bit to 32-bit
|
||||
;; If 255 is treated as an i8, it is -1.
|
||||
;; Result should be -1 (0xFFFFFFFF).
|
||||
;; Without this proposal, this opcode (0xC0) causes a validation error.
|
||||
i32.extend8_s
|
||||
|
||||
;; Check if result is -1
|
||||
i32.const -1
|
||||
i32.eq
|
||||
)
|
||||
|
||||
(export "finish" (func $test_sign_ext))
|
||||
)
|
||||
1
src/test/app/wasm_fixtures/wat/proposal_stringref.wat
Normal file
1
src/test/app/wasm_fixtures/wat/proposal_stringref.wat
Normal file
@@ -0,0 +1 @@
|
||||
;;hard to generate
|
||||
15
src/test/app/wasm_fixtures/wat/proposal_tail_call.wat
Normal file
15
src/test/app/wasm_fixtures/wat/proposal_tail_call.wat
Normal file
@@ -0,0 +1,15 @@
|
||||
(module
|
||||
;; Define a simple function we can tail-call
|
||||
(func $target (result i32)
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(func $finish (result i32)
|
||||
;; Try to use the 'return_call' instruction (Opcode 0x12)
|
||||
;; If Tail Call proposal is disabled, this fails to Compile/Validate.
|
||||
;; If enabled, it jumps to $target, which returns 1.
|
||||
return_call $target
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
22
src/test/app/wasm_fixtures/wat/start_loop.wat
Normal file
22
src/test/app/wasm_fixtures/wat/start_loop.wat
Normal file
@@ -0,0 +1,22 @@
|
||||
(module
|
||||
;; Function 1: The Infinite Loop
|
||||
(func $run_forever
|
||||
(loop $infinite
|
||||
br $infinite
|
||||
)
|
||||
)
|
||||
|
||||
;; Function 2: Finish
|
||||
(func $finish (result i32)
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
;; 1. EXPORT the functions (optional, if you want to call them later)
|
||||
(export "start" (func $run_forever))
|
||||
(export "finish" (func $finish))
|
||||
|
||||
;; 2. The special start section
|
||||
;; This tells the VM: "Run function $run_forever immediately
|
||||
;; when this module is instantiated."
|
||||
(start $run_forever)
|
||||
)
|
||||
10
src/test/app/wasm_fixtures/wat/table_0_elements.wat
Normal file
10
src/test/app/wasm_fixtures/wat/table_0_elements.wat
Normal file
@@ -0,0 +1,10 @@
|
||||
(module
|
||||
;; Define a table with exactly 0 entries
|
||||
(table 0 funcref)
|
||||
|
||||
;; Standard finish function
|
||||
(func $finish (result i32)
|
||||
i32.const 1
|
||||
)
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
24
src/test/app/wasm_fixtures/wat/table_2_tables.wat
Normal file
24
src/test/app/wasm_fixtures/wat/table_2_tables.wat
Normal file
@@ -0,0 +1,24 @@
|
||||
(module
|
||||
;; Define a dummy function to put in the tables
|
||||
(func $dummy)
|
||||
|
||||
;; TABLE 0: The default table (allowed in MVP)
|
||||
;; Size: 1 initial, 1 max
|
||||
(table $t0 1 1 funcref)
|
||||
|
||||
;; Initialize Table 0 at index 0
|
||||
(elem (table $t0) (i32.const 0) $dummy)
|
||||
|
||||
;; TABLE 1: The second table (Requires Reference Types proposal)
|
||||
;; If strict MVP is enforced, the parser should error here.
|
||||
(table $t1 1 1 funcref)
|
||||
|
||||
;; Initialize Table 1 at index 0
|
||||
(elem (table $t1) (i32.const 0) $dummy)
|
||||
|
||||
(func $finish (result i32)
|
||||
;; If we successfully loaded a module with 2 tables, return 1.
|
||||
i32.const 1
|
||||
)
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
25
src/test/app/wasm_fixtures/wat/table_64_elements.wat
Normal file
25
src/test/app/wasm_fixtures/wat/table_64_elements.wat
Normal file
@@ -0,0 +1,25 @@
|
||||
(module
|
||||
;; Define a table with exactly 64 entries
|
||||
(table 64 funcref)
|
||||
|
||||
;; A dummy function to reference
|
||||
(func $dummy)
|
||||
|
||||
;; Initialize the table at offset 0 with 64 references to $dummy
|
||||
(elem (i32.const 0)
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 8
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 16
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 24
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 32
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 40
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 48
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 56
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 64
|
||||
)
|
||||
|
||||
;; Standard finish function
|
||||
(func $finish (result i32)
|
||||
i32.const 1
|
||||
)
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
25
src/test/app/wasm_fixtures/wat/table_65_elements.wat
Normal file
25
src/test/app/wasm_fixtures/wat/table_65_elements.wat
Normal file
@@ -0,0 +1,25 @@
|
||||
(module
|
||||
;; Define a table with exactly 65 entries
|
||||
(table 65 funcref)
|
||||
|
||||
;; A dummy function to reference
|
||||
(func $dummy)
|
||||
|
||||
;; Initialize the table at offset 0 with 65 references to $dummy
|
||||
(elem (i32.const 0)
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 8
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 16
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 24
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 32
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 40
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 48
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 56
|
||||
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 64
|
||||
$dummy ;; 65 (The one that breaks the camel's back)
|
||||
)
|
||||
|
||||
(func $finish (result i32)
|
||||
i32.const 1
|
||||
)
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
15
src/test/app/wasm_fixtures/wat/table_uint_max.wat
Normal file
15
src/test/app/wasm_fixtures/wat/table_uint_max.wat
Normal file
@@ -0,0 +1,15 @@
|
||||
(module
|
||||
;; Definition: (table <min> <optional_max> <type>)
|
||||
;; We use 0xFFFFFFFF (4,294,967,295), which is the unsigned equivalent of -1.
|
||||
;; This tests if the runtime handles the maximum possible u32 value
|
||||
;; without integer overflows or attempting a massive allocation.
|
||||
;;
|
||||
;; Note that using -1 as the table size cannot be parsed by wasm-tools or wat2wasm
|
||||
(table 0xFFFFFFFF funcref)
|
||||
|
||||
(func $finish (result i32)
|
||||
;; If the module loads despite the massive table, return 1.
|
||||
i32.const 1
|
||||
)
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
15
src/test/app/wasm_fixtures/wat/trap_divide_by_0.wat
Normal file
15
src/test/app/wasm_fixtures/wat/trap_divide_by_0.wat
Normal file
@@ -0,0 +1,15 @@
|
||||
(module
|
||||
(func $finish (export "finish") (result i32)
|
||||
;; Setup for Requirement 2: Divide an i32 by 0
|
||||
i32.const 42 ;; Push numerator
|
||||
i32.const 0 ;; Push denominator (0)
|
||||
i32.div_s ;; Perform signed division (42 / 0)
|
||||
|
||||
;; --- NOTE: Execution usually traps (crashes) at the line above ---
|
||||
|
||||
;; Logic to satisfy Requirement 1: Return i32 = 1
|
||||
;; If execution continued, we would drop the division result and return 1
|
||||
drop ;; Clear the stack
|
||||
i32.const 1 ;; Push the return value
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
(module
|
||||
;; Define a table with 1 slot
|
||||
(table 1 funcref)
|
||||
|
||||
;; Define Type A: Takes nothing, returns nothing
|
||||
(type $type_void (func))
|
||||
|
||||
;; Define Type B: Takes nothing, returns i32
|
||||
(type $type_i32 (func (result i32)))
|
||||
|
||||
;; Define a function of Type A
|
||||
(func $void_func (type $type_void)
|
||||
nop
|
||||
)
|
||||
|
||||
;; Put Type A function into Table[0]
|
||||
(elem (i32.const 0) $void_func)
|
||||
|
||||
(func $finish (result i32)
|
||||
;; Attempt to call Index 0, but CLAIM we expect Type B (result i32).
|
||||
;; The function at Index 0 matches Type A.
|
||||
;; TRAP: "indirect call type mismatch"
|
||||
|
||||
;; 1. Push the table index (0) onto the stack
|
||||
i32.const 0
|
||||
|
||||
;; 2. Call indirect using Type B signature.
|
||||
;; This pops the index (0) from the stack.
|
||||
call_indirect (type $type_i32)
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
18
src/test/app/wasm_fixtures/wat/trap_int_overflow.wat
Normal file
18
src/test/app/wasm_fixtures/wat/trap_int_overflow.wat
Normal file
@@ -0,0 +1,18 @@
|
||||
(module
|
||||
(func $test_int_overflow (result i32)
|
||||
;; 1. Push INT_MIN (-2147483648)
|
||||
;; In Hex: 0x80000000
|
||||
i32.const -2147483648
|
||||
|
||||
;; 2. Push -1
|
||||
i32.const -1
|
||||
|
||||
;; 3. Signed Division
|
||||
;; This specific case is the ONLY integer arithmetic operation
|
||||
;; (besides divide by zero) that traps in the spec.
|
||||
;; Result would be +2147483648, which is too big for signed i32.
|
||||
i32.div_s
|
||||
)
|
||||
|
||||
(export "finish" (func $test_int_overflow))
|
||||
)
|
||||
22
src/test/app/wasm_fixtures/wat/trap_null_call.wat
Normal file
22
src/test/app/wasm_fixtures/wat/trap_null_call.wat
Normal file
@@ -0,0 +1,22 @@
|
||||
(module
|
||||
;; Table size is 1, so Index 0 is VALID bounds.
|
||||
;; However, we do NOT initialize it, so it contains 'ref.null'.
|
||||
(table 1 funcref)
|
||||
|
||||
(type $t (func (result i32)))
|
||||
|
||||
(func $finish (result i32)
|
||||
;; Call Index 0.
|
||||
;; Bounds check passes (0 < 1).
|
||||
;; Null check fails.
|
||||
;; TRAP: "uninitialized element" or "undefined element"
|
||||
|
||||
;; 1. Push the index (0) onto the stack first
|
||||
i32.const 0
|
||||
|
||||
;; 2. Perform the call. This pops the index.
|
||||
call_indirect (type $t)
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
12
src/test/app/wasm_fixtures/wat/trap_unreachable.wat
Normal file
12
src/test/app/wasm_fixtures/wat/trap_unreachable.wat
Normal file
@@ -0,0 +1,12 @@
|
||||
(module
|
||||
(func $finish (result i32)
|
||||
;; This instruction explicitly causes a trap.
|
||||
;; It consumes no fuel (beyond the instruction itself) and stops execution.
|
||||
unreachable
|
||||
|
||||
;; This code is dead and never reached
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
38
src/test/app/wasm_fixtures/wat/wasi_get_time.wat
Normal file
38
src/test/app/wasm_fixtures/wat/wasi_get_time.wat
Normal file
@@ -0,0 +1,38 @@
|
||||
(module
|
||||
;; Import clock_time_get from WASI
|
||||
;; Signature: (param clock_id precision return_ptr) (result errno)
|
||||
(import "wasi_snapshot_preview1" "clock_time_get"
|
||||
(func $clock_time_get (param i32 i64 i32) (result i32))
|
||||
)
|
||||
|
||||
(memory 1)
|
||||
(export "memory" (memory 0))
|
||||
|
||||
(func $finish (result i32)
|
||||
;; We will store the timestamp (a 64-bit integer) at address 0.
|
||||
;; No setup required in memory beforehand!
|
||||
|
||||
;; Call the function
|
||||
(call $clock_time_get
|
||||
(i32.const 0) ;; clock_id: 0 = Realtime (Wallclock)
|
||||
(i64.const 1000) ;; precision: 1000ns (hint to OS)
|
||||
(i32.const 0) ;; result_ptr: Write the time to address 0
|
||||
)
|
||||
|
||||
;; The function returns an 'errno' (error code).
|
||||
;; 0 = Success. Anything else = Error.
|
||||
|
||||
;; Check if errno (top of stack) is 0
|
||||
i32.eqz
|
||||
if (result i32)
|
||||
;; Success! The time is now stored in heap[0..8].
|
||||
;; We return 1 as requested.
|
||||
i32.const 1
|
||||
else
|
||||
;; Failed (maybe WASI is disabled or clock is missing)
|
||||
i32.const -1
|
||||
end
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
59
src/test/app/wasm_fixtures/wat/wasi_print.wat
Normal file
59
src/test/app/wasm_fixtures/wat/wasi_print.wat
Normal file
@@ -0,0 +1,59 @@
|
||||
(module
|
||||
;; Import WASI fd_write
|
||||
;; Signature: (fd, iovs_ptr, iovs_len, nwritten_ptr) -> errno
|
||||
(import "wasi_snapshot_preview1" "fd_write"
|
||||
(func $fd_write (param i32 i32 i32 i32) (result i32))
|
||||
)
|
||||
|
||||
(memory 1)
|
||||
(export "memory" (memory 0))
|
||||
|
||||
;; --- DATA SEGMENTS ---
|
||||
|
||||
;; 1. The String Data "Hello\n" placed at offset 16
|
||||
;; We assume offset 0-16 is reserved for the IOVec struct
|
||||
(data (i32.const 16) "Hello\n")
|
||||
|
||||
;; 2. The IO Vector (struct iovec) placed at offset 0
|
||||
;; Structure: { buf_ptr: u32, buf_len: u32 }
|
||||
|
||||
;; Field 1: buf_ptr = 16 (Location of "Hello\n")
|
||||
;; Encoded in little-endian: 10 00 00 00
|
||||
(data (i32.const 0) "\10\00\00\00")
|
||||
|
||||
;; Field 2: buf_len = 6 (Length of "Hello\n")
|
||||
;; Encoded in little-endian: 06 00 00 00
|
||||
(data (i32.const 4) "\06\00\00\00")
|
||||
|
||||
(func $finish (result i32)
|
||||
(local $nwritten_ptr i32)
|
||||
|
||||
;; We will ask WASI to write the "number of bytes written" to address 24
|
||||
;; (safely after our string data)
|
||||
i32.const 24
|
||||
local.set $nwritten_ptr
|
||||
|
||||
;; Call fd_write
|
||||
(call $fd_write
|
||||
(i32.const 1) ;; fd: 1 = STDOUT
|
||||
(i32.const 0) ;; iovs_ptr: Address 0 (where we defined the struct)
|
||||
(i32.const 1) ;; iovs_len: We are passing 1 vector
|
||||
(local.get $nwritten_ptr) ;; nwritten_ptr: Address 24
|
||||
)
|
||||
|
||||
;; The function returns an 'errno' (i32).
|
||||
;; 0 means Success.
|
||||
|
||||
;; Check if errno == 0
|
||||
i32.eqz
|
||||
if (result i32)
|
||||
;; Success: Return 1
|
||||
i32.const 1
|
||||
else
|
||||
;; Failure: Return -1
|
||||
i32.const -1
|
||||
end
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
22
src/test/app/wasm_fixtures/wat/wide_arithmetic.wat
Normal file
22
src/test/app/wasm_fixtures/wat/wide_arithmetic.wat
Normal file
@@ -0,0 +1,22 @@
|
||||
(module
|
||||
(func $finish (result i32)
|
||||
;; 1. Push operands
|
||||
i64.const 1
|
||||
i64.const 2
|
||||
|
||||
;; 2. Execute Wide Multiplication
|
||||
;; If the feature is DISABLED, the parser/validator will trap here
|
||||
;; with "unknown instruction" or "invalid opcode".
|
||||
;; Input: [i64, i64] -> Output: [i64, i64]
|
||||
i64.mul_wide_u
|
||||
|
||||
;; 3. Clean up the stack (drop the two i64 results)
|
||||
drop
|
||||
drop
|
||||
|
||||
;; 4. Return 1 to signal that validation passed
|
||||
i32.const 1
|
||||
)
|
||||
|
||||
(export "finish" (func $finish))
|
||||
)
|
||||
@@ -1334,6 +1334,75 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_log10()
|
||||
{
|
||||
auto const scale = Number::getMantissaScale();
|
||||
testcase << "test_lg " << to_string(scale);
|
||||
|
||||
using Case = std::tuple<Number, Number>;
|
||||
auto test = [this](auto const& c) {
|
||||
for (auto const& [x, z] : c)
|
||||
{
|
||||
auto const result = log10(x);
|
||||
std::stringstream ss;
|
||||
ss << "lg(" << x << ") = " << result << ". Expected: " << z;
|
||||
// std::cout << ss.str() << std::endl;
|
||||
BEAST_EXPECTS(result == z, ss.str());
|
||||
}
|
||||
};
|
||||
|
||||
auto const cSmall = std::to_array<Case>(
|
||||
{{Number{2}, Number{3'010'299'956'639'811ll, -16}},
|
||||
{Number{2'000'000}, Number{6'301'029'995'663'985ll, -15}},
|
||||
{Number{2, -30}, Number{-2'969'897'000'433'602ll, -14}},
|
||||
{Number{1}, Number{0}},
|
||||
{Number{1'000'000'000'000'000ll}, Number{15}},
|
||||
{Number{5625, -4}, Number{-2'498'774'732'165'998, -16}}});
|
||||
|
||||
auto const cLarge = std::to_array<Case>(
|
||||
{{Number{false, Number::maxMantissa() - 9, -1, Number::normalized{}},
|
||||
Number{false, 1'746'901'684'478'673'451ll, -17, Number::normalized{}}},
|
||||
{Number{false, Number::maxMantissa() - 9, 0, Number::normalized{}},
|
||||
Number{false, 1'846'901'684'478'673'451ll, -17, Number::normalized{}}},
|
||||
{Number{Number::maxRep}, Number{false, 1'861'728'612'932'620'011ll, -17, Number::normalized{}}}});
|
||||
|
||||
if (Number::getMantissaScale() == MantissaRange::small)
|
||||
{
|
||||
test(cSmall);
|
||||
}
|
||||
else
|
||||
{
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
test(cLarge);
|
||||
}
|
||||
|
||||
{
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
log10(Number{-2});
|
||||
}
|
||||
catch (std::runtime_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
BEAST_EXPECT(caught);
|
||||
caught = false;
|
||||
|
||||
try
|
||||
{
|
||||
log10(Number());
|
||||
}
|
||||
catch (std::runtime_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
BEAST_EXPECT(caught);
|
||||
caught = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -1361,6 +1430,7 @@ public:
|
||||
test_truncate();
|
||||
testRounding();
|
||||
testInt64();
|
||||
test_log10();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/shamap/TreeNodeCache.h>
|
||||
|
||||
@@ -91,7 +92,7 @@ class Validations;
|
||||
class RCLValidationsAdaptor;
|
||||
using RCLValidations = Validations<RCLValidationsAdaptor>;
|
||||
|
||||
class Application : public beast::PropertyStream::Source
|
||||
class Application : public ServiceRegistry, public beast::PropertyStream::Source
|
||||
{
|
||||
public:
|
||||
/* VFALCO NOTE
|
||||
@@ -146,92 +147,12 @@ public:
|
||||
virtual boost::asio::io_context&
|
||||
getIOContext() = 0;
|
||||
|
||||
virtual CollectorManager&
|
||||
getCollectorManager() = 0;
|
||||
virtual Family&
|
||||
getNodeFamily() = 0;
|
||||
virtual TimeKeeper&
|
||||
timeKeeper() = 0;
|
||||
virtual JobQueue&
|
||||
getJobQueue() = 0;
|
||||
virtual NodeCache&
|
||||
getTempNodeCache() = 0;
|
||||
virtual CachedSLEs&
|
||||
cachedSLEs() = 0;
|
||||
virtual AmendmentTable&
|
||||
getAmendmentTable() = 0;
|
||||
virtual HashRouter&
|
||||
getHashRouter() = 0;
|
||||
virtual LoadFeeTrack&
|
||||
getFeeTrack() = 0;
|
||||
virtual LoadManager&
|
||||
getLoadManager() = 0;
|
||||
virtual Overlay&
|
||||
overlay() = 0;
|
||||
virtual TxQ&
|
||||
getTxQ() = 0;
|
||||
virtual ValidatorList&
|
||||
validators() = 0;
|
||||
virtual ValidatorSite&
|
||||
validatorSites() = 0;
|
||||
virtual ManifestCache&
|
||||
validatorManifests() = 0;
|
||||
virtual ManifestCache&
|
||||
publisherManifests() = 0;
|
||||
virtual Cluster&
|
||||
cluster() = 0;
|
||||
virtual PeerReservationTable&
|
||||
peerReservations() = 0;
|
||||
virtual RCLValidations&
|
||||
getValidations() = 0;
|
||||
virtual NodeStore::Database&
|
||||
getNodeStore() = 0;
|
||||
virtual InboundLedgers&
|
||||
getInboundLedgers() = 0;
|
||||
virtual InboundTransactions&
|
||||
getInboundTransactions() = 0;
|
||||
|
||||
virtual TaggedCache<uint256, AcceptedLedger>&
|
||||
getAcceptedLedgerCache() = 0;
|
||||
|
||||
virtual LedgerMaster&
|
||||
getLedgerMaster() = 0;
|
||||
virtual LedgerCleaner&
|
||||
getLedgerCleaner() = 0;
|
||||
virtual LedgerReplayer&
|
||||
getLedgerReplayer() = 0;
|
||||
virtual NetworkOPs&
|
||||
getOPs() = 0;
|
||||
virtual OrderBookDB&
|
||||
getOrderBookDB() = 0;
|
||||
virtual ServerHandler&
|
||||
getServerHandler() = 0;
|
||||
virtual TransactionMaster&
|
||||
getMasterTransaction() = 0;
|
||||
virtual perf::PerfLog&
|
||||
getPerfLog() = 0;
|
||||
|
||||
virtual std::pair<PublicKey, SecretKey> const&
|
||||
nodeIdentity() = 0;
|
||||
|
||||
virtual std::optional<PublicKey const>
|
||||
getValidationPublicKey() const = 0;
|
||||
|
||||
virtual Resource::Manager&
|
||||
getResourceManager() = 0;
|
||||
virtual PathRequests&
|
||||
getPathRequests() = 0;
|
||||
virtual SHAMapStore&
|
||||
getSHAMapStore() = 0;
|
||||
virtual PendingSaves&
|
||||
pendingSaves() = 0;
|
||||
virtual OpenLedger&
|
||||
openLedger() = 0;
|
||||
virtual OpenLedger const&
|
||||
openLedger() const = 0;
|
||||
virtual RelationalDatabase&
|
||||
getRelationalDatabase() = 0;
|
||||
|
||||
virtual std::chrono::milliseconds
|
||||
getIOLatency() = 0;
|
||||
|
||||
|
||||
@@ -53,7 +53,17 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx)
|
||||
return {nullptr, tecOBJECT_NOT_FOUND};
|
||||
|
||||
if (hasExpired(ctx.view, (*offerSLE)[~sfExpiration]))
|
||||
return {nullptr, tecEXPIRED};
|
||||
{
|
||||
// Before fixExpiredNFTokenOfferRemoval amendment, expired
|
||||
// offers caused tecEXPIRED in preclaim, leaving them on ledger
|
||||
// forever. After the amendment, we allow expired offers to
|
||||
// reach doApply() where they get deleted and tecEXPIRED is
|
||||
// returned.
|
||||
if (!ctx.view.rules().enabled(fixExpiredNFTokenOfferRemoval))
|
||||
return {nullptr, tecEXPIRED};
|
||||
// Amendment enabled: return the expired offer to be handled in
|
||||
// doApply
|
||||
}
|
||||
|
||||
if ((*offerSLE)[sfAmount].negative())
|
||||
return {nullptr, temBAD_OFFER};
|
||||
@@ -299,7 +309,7 @@ NFTokenAcceptOffer::pay(AccountID const& from, AccountID const& to, STAmount con
|
||||
{
|
||||
// This should never happen, but it's easy and quick to check.
|
||||
if (amount < beast::zero)
|
||||
return tecINTERNAL;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const result = accountSend(view(), from, to, amount, j_);
|
||||
|
||||
@@ -410,6 +420,39 @@ NFTokenAcceptOffer::doApply()
|
||||
auto bo = loadToken(ctx_.tx[~sfNFTokenBuyOffer]);
|
||||
auto so = loadToken(ctx_.tx[~sfNFTokenSellOffer]);
|
||||
|
||||
// With fixExpiredNFTokenOfferRemoval amendment, check for expired offers
|
||||
// and delete them, returning tecEXPIRED. This ensures expired offers
|
||||
// are properly cleaned up from the ledger.
|
||||
if (view().rules().enabled(fixExpiredNFTokenOfferRemoval))
|
||||
{
|
||||
bool foundExpired = false;
|
||||
|
||||
auto const deleteOfferIfExpired = [this, &foundExpired](std::shared_ptr<SLE> const& offer) -> TER {
|
||||
if (offer && hasExpired(view(), (*offer)[~sfExpiration]))
|
||||
{
|
||||
JLOG(j_.trace()) << "Offer is expired, deleting: " << offer->key();
|
||||
if (!nft::deleteTokenOffer(view(), offer))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete expired offer '" << offer->key() << "': ignoring";
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
JLOG(j_.trace()) << "Deleted offer " << offer->key();
|
||||
foundExpired = true;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
};
|
||||
|
||||
if (auto const r = deleteOfferIfExpired(bo); !isTesSuccess(r))
|
||||
return r;
|
||||
if (auto const r = deleteOfferIfExpired(so); !isTesSuccess(r))
|
||||
return r;
|
||||
|
||||
if (foundExpired)
|
||||
return tecEXPIRED;
|
||||
}
|
||||
|
||||
if (bo && !nft::deleteTokenOffer(view(), bo))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
|
||||
507
src/xrpld/app/wasm/HostFunc.h
Normal file
507
src/xrpld/app/wasm/HostFunc.h
Normal file
@@ -0,0 +1,507 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/app/wasm/ParamsHelper.h>
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum class HostFunctionError : int32_t {
|
||||
INTERNAL = -1,
|
||||
FIELD_NOT_FOUND = -2,
|
||||
BUFFER_TOO_SMALL = -3,
|
||||
NO_ARRAY = -4,
|
||||
NOT_LEAF_FIELD = -5,
|
||||
LOCATOR_MALFORMED = -6,
|
||||
SLOT_OUT_RANGE = -7,
|
||||
SLOTS_FULL = -8,
|
||||
EMPTY_SLOT = -9,
|
||||
LEDGER_OBJ_NOT_FOUND = -10,
|
||||
DECODING = -11,
|
||||
DATA_FIELD_TOO_LARGE = -12,
|
||||
POINTER_OUT_OF_BOUNDS = -13,
|
||||
NO_MEM_EXPORTED = -14,
|
||||
INVALID_PARAMS = -15,
|
||||
INVALID_ACCOUNT = -16,
|
||||
INVALID_FIELD = -17,
|
||||
INDEX_OUT_OF_BOUNDS = -18,
|
||||
FLOAT_INPUT_MALFORMED = -19,
|
||||
FLOAT_COMPUTATION_ERROR = -20,
|
||||
NO_RUNTIME = -21,
|
||||
OUT_OF_GAS = -22,
|
||||
};
|
||||
|
||||
inline int32_t
|
||||
HfErrorToInt(HostFunctionError e)
|
||||
{
|
||||
return static_cast<int32_t>(e);
|
||||
}
|
||||
|
||||
namespace wasm_float {
|
||||
|
||||
std::string
|
||||
floatToString(Slice const& data);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromIntImpl(int64_t x, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromUintImpl(uint64_t x, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode);
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
floatCompareImpl(Slice const& x, Slice const& y);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatAddImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatDivideImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatRootImpl(Slice const& x, int32_t n, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatPowerImpl(Slice const& x, int32_t n, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatLogImpl(Slice const& x, int32_t mode);
|
||||
|
||||
} // namespace wasm_float
|
||||
|
||||
struct HostFunctions
|
||||
{
|
||||
beast::Journal j_;
|
||||
|
||||
HostFunctions(beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) : j_(j)
|
||||
{
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
virtual void
|
||||
setRT(void const*)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void const*
|
||||
getRT() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::int64_t
|
||||
getGas()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
setGas(std::int64_t)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
getJournal()
|
||||
{
|
||||
return j_;
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getLedgerSqn()
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getParentLedgerTime()
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Hash, HostFunctionError>
|
||||
getParentLedgerHash()
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<uint32_t, HostFunctionError>
|
||||
getBaseFee()
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(uint256 const& amendmentId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(std::string_view const& amendmentName)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjField(SField const& fname)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getTxNestedField(Slice const& locator)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(Slice const& locator)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
accountKeylet(AccountID const& account)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
ammKeylet(Asset const& issue1, Asset const& issue2)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
checkKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
didKeylet(AccountID const& account)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
delegateKeylet(AccountID const& account, AccountID const& authorize)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
depositPreauthKeylet(AccountID const& account, AccountID const& authorize)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
mptokenKeylet(MPTID const& mptid, AccountID const& holder)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
nftOfferKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
offerKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t docId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
signersKeylet(AccountID const& account)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
ticketKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
vaultKeylet(AccountID const& account, std::uint32_t seq)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getNFTIssuer(uint256 const& nftId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTTaxon(uint256 const& nftId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getNFTFlags(uint256 const& nftId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getNFTTransferFee(uint256 const& nftId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTSerial(uint256 const& nftId)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
trace(std::string_view const& msg, Slice const& data, bool asHex)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceNum(std::string_view const& msg, int64_t data)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceAccount(std::string_view const& msg, AccountID const& account)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceFloat(std::string_view const& msg, Slice const& data)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceAmount(std::string_view const& msg, STAmount const& amount)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromInt(int64_t x, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromUint(uint64_t x, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatSet(int64_t mantissa, int32_t exponent, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
floatCompare(Slice const& x, Slice const& y)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatAdd(Slice const& x, Slice const& y, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatSubtract(Slice const& x, Slice const& y, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatMultiply(Slice const& x, Slice const& y, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatDivide(Slice const& x, Slice const& y, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatRoot(Slice const& x, int32_t n, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatPower(Slice const& x, int32_t n, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatLog(Slice const& x, int32_t mode)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual ~HostFunctions() = default;
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
285
src/xrpld/app/wasm/HostFuncImpl.h
Normal file
285
src/xrpld/app/wasm/HostFuncImpl.h
Normal file
@@ -0,0 +1,285 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/app/tx/detail/ApplyContext.h>
|
||||
#include <xrpld/app/wasm/HostFunc.h>
|
||||
|
||||
namespace xrpl {
|
||||
class WasmHostFunctionsImpl : public HostFunctions
|
||||
{
|
||||
ApplyContext& ctx;
|
||||
Keylet leKey;
|
||||
std::shared_ptr<SLE const> currentLedgerObj = nullptr;
|
||||
bool isLedgerObjCached = false;
|
||||
|
||||
static int constexpr MAX_CACHE = 256;
|
||||
std::array<std::shared_ptr<SLE const>, MAX_CACHE> cache;
|
||||
std::optional<Bytes> data_;
|
||||
|
||||
void const* rt_ = nullptr;
|
||||
|
||||
Expected<std::shared_ptr<SLE const>, HostFunctionError>
|
||||
getCurrentLedgerObj()
|
||||
{
|
||||
if (!isLedgerObjCached)
|
||||
{
|
||||
isLedgerObjCached = true;
|
||||
currentLedgerObj = ctx.view().read(leKey);
|
||||
}
|
||||
if (currentLedgerObj)
|
||||
return currentLedgerObj;
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
normalizeCacheIndex(int32_t cacheIdx)
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
if (!cache[cacheIdx])
|
||||
return Unexpected(HostFunctionError::EMPTY_SLOT);
|
||||
return cacheIdx;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void
|
||||
log(std::string_view const& msg, F&& dataFn)
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
auto& j = std::cerr;
|
||||
#else
|
||||
if (!getJournal().active(beast::severities::kTrace))
|
||||
return;
|
||||
auto j = getJournal().trace();
|
||||
#endif
|
||||
j << "WasmTrace[" << to_short_string(leKey.key) << "]: " << msg << " " << dataFn();
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
j << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
WasmHostFunctionsImpl(ApplyContext& ct, Keylet const& leKey) : HostFunctions(ct.journal), ctx(ct), leKey(leKey)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
setRT(void const* rt) override
|
||||
{
|
||||
rt_ = rt;
|
||||
}
|
||||
|
||||
virtual void const*
|
||||
getRT() const override
|
||||
{
|
||||
return rt_;
|
||||
}
|
||||
|
||||
std::optional<Bytes> const&
|
||||
getData() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getLedgerSqn() override;
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getParentLedgerTime() override;
|
||||
|
||||
Expected<Hash, HostFunctionError>
|
||||
getParentLedgerHash() override;
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getBaseFee() override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(uint256 const& amendmentId) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(std::string_view const& amendmentName) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjField(SField const& fname) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxNestedField(Slice const& locator) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(Slice const& locator) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) override;
|
||||
|
||||
Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
accountKeylet(AccountID const& account) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
ammKeylet(Asset const& issue1, Asset const& issue2) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
checkKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
didKeylet(AccountID const& account) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
delegateKeylet(AccountID const& account, AccountID const& authorize) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
depositPreauthKeylet(AccountID const& account, AccountID const& authorize) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
mptokenKeylet(MPTID const& mptid, AccountID const& holder) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
nftOfferKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
offerKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t docId) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
signersKeylet(AccountID const& account) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
ticketKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
vaultKeylet(AccountID const& account, std::uint32_t seq) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getNFTIssuer(uint256 const& nftId) override;
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTTaxon(uint256 const& nftId) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getNFTFlags(uint256 const& nftId) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getNFTTransferFee(uint256 const& nftId) override;
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTSerial(uint256 const& nftId) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
trace(std::string_view const& msg, Slice const& data, bool asHex) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceNum(std::string_view const& msg, int64_t data) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceAccount(std::string_view const& msg, AccountID const& account) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceFloat(std::string_view const& msg, Slice const& data) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceAmount(std::string_view const& msg, STAmount const& amount) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromInt(int64_t x, int32_t mode) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromUint(uint64_t x, int32_t mode) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatSet(int64_t mantissa, int32_t exponent, int32_t mode) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
floatCompare(Slice const& x, Slice const& y) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatAdd(Slice const& x, Slice const& y, int32_t mode) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatSubtract(Slice const& x, Slice const& y, int32_t mode) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatMultiply(Slice const& x, Slice const& y, int32_t mode) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatDivide(Slice const& x, Slice const& y, int32_t mode) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatRoot(Slice const& x, int32_t n, int32_t mode) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatPower(Slice const& x, int32_t n, int32_t mode) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatLog(Slice const& x, int32_t mode) override;
|
||||
};
|
||||
|
||||
namespace wasm_float {
|
||||
|
||||
// The range for the mantissa and exponent when normalized
|
||||
static std::int64_t constexpr wasmMinMantissa = 1'000'000'000'000'000ll;
|
||||
static std::int64_t constexpr wasmMaxMantissa = wasmMinMantissa * 10 - 1;
|
||||
static int constexpr wasmMinExponent = -96;
|
||||
static int constexpr wasmMaxExponent = 80;
|
||||
|
||||
} // namespace wasm_float
|
||||
|
||||
} // namespace xrpl
|
||||
258
src/xrpld/app/wasm/HostFuncWrapper.h
Normal file
258
src/xrpld/app/wasm/HostFuncWrapper.h
Normal file
@@ -0,0 +1,258 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/app/wasm/WasmiVM.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
using getLedgerSqn_proto = int32_t(uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerSqn_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getParentLedgerTime_proto = int32_t(uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getParentLedgerTime_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getParentLedgerHash_proto = int32_t(uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getParentLedgerHash_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getBaseFee_proto = int32_t(uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getBaseFee_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using isAmendmentEnabled_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
isAmendmentEnabled_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using cacheLedgerObj_proto = int32_t(uint8_t const*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
cacheLedgerObj_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getTxField_proto = int32_t(int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getTxField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getCurrentLedgerObjField_proto = int32_t(int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getCurrentLedgerObjField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getLedgerObjField_proto = int32_t(int32_t, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerObjField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getTxNestedField_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getTxNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getCurrentLedgerObjNestedField_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getCurrentLedgerObjNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getLedgerObjNestedField_proto = int32_t(int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerObjNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getTxArrayLen_proto = int32_t(int32_t);
|
||||
wasm_trap_t*
|
||||
getTxArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getCurrentLedgerObjArrayLen_proto = int32_t(int32_t);
|
||||
wasm_trap_t*
|
||||
getCurrentLedgerObjArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getLedgerObjArrayLen_proto = int32_t(int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerObjArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getTxNestedArrayLen_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getTxNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getCurrentLedgerObjNestedArrayLen_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getCurrentLedgerObjNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getLedgerObjNestedArrayLen_proto = int32_t(int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerObjNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using updateData_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
updateData_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using checkSignature_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
checkSignature_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using computeSha512HalfHash_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
computeSha512HalfHash_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using accountKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
accountKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using ammKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
ammKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using checkKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
checkKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using credentialKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
credentialKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using delegateKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
delegateKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using depositPreauthKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
depositPreauthKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using didKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
didKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using escrowKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
escrowKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using lineKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
lineKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using mptIssuanceKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
mptIssuanceKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using mptokenKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
mptokenKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using nftOfferKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
nftOfferKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using offerKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
offerKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using oracleKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
oracleKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using paychanKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
paychanKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using permissionedDomainKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
permissionedDomainKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using signersKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
signersKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using ticketKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
ticketKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using vaultKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
vaultKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFT_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFT_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTIssuer_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTIssuer_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTTaxon_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTTaxon_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTFlags_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTFlags_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTTransferFee_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTTransferFee_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTSerial_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTSerial_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using trace_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
trace_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using traceNum_proto = int32_t(uint8_t const*, int32_t, int64_t);
|
||||
wasm_trap_t*
|
||||
traceNum_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using traceAccount_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
traceAccount_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using traceFloat_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
traceFloat_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using traceAmount_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
traceAmount_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatFromInt_proto = int32_t(int64_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatFromInt_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatFromUint_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatFromUint_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatSet_proto = int32_t(int32_t, int64_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatSet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatCompare_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
floatCompare_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatAdd_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatAdd_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatSubtract_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatSubtract_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatMultiply_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatMultiply_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatDivide_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatDivide_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatRoot_proto = int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatRoot_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatPower_proto = int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatPower_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatLog_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatLog_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
} // namespace xrpl
|
||||
269
src/xrpld/app/wasm/ParamsHelper.h
Normal file
269
src/xrpld/app/wasm/ParamsHelper.h
Normal file
@@ -0,0 +1,269 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <boost/function_types/function_arity.hpp>
|
||||
#include <boost/function_types/parameter_types.hpp>
|
||||
#include <boost/function_types/result_type.hpp>
|
||||
#include <boost/mpl/vector.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace bft = boost::function_types;
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
using Bytes = std::vector<std::uint8_t>;
|
||||
using Hash = xrpl::uint256;
|
||||
|
||||
struct wmem
|
||||
{
|
||||
std::uint8_t* p = nullptr;
|
||||
std::size_t s = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct WasmResult
|
||||
{
|
||||
T result;
|
||||
int64_t cost;
|
||||
};
|
||||
typedef WasmResult<int32_t> EscrowResult;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum WasmTypes { WT_I32, WT_I64, WT_U8V };
|
||||
|
||||
struct WasmImportFunc
|
||||
{
|
||||
std::string name;
|
||||
std::optional<WasmTypes> result;
|
||||
std::vector<WasmTypes> params;
|
||||
// void* udata = nullptr;
|
||||
// wasm_func_callback_with_env_t
|
||||
void* wrap = nullptr;
|
||||
uint32_t gas = 0;
|
||||
};
|
||||
|
||||
typedef std::pair<void*, WasmImportFunc> WasmUserData;
|
||||
typedef std::vector<WasmUserData> ImportVec;
|
||||
|
||||
#define WASM_IMPORT_FUNC(v, f, ...) WasmImpFunc<f##_proto>(v, #f, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__)
|
||||
|
||||
#define WASM_IMPORT_FUNC2(v, f, n, ...) WasmImpFunc<f##_proto>(v, n, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__)
|
||||
|
||||
template <int N, int C, typename mpl>
|
||||
void
|
||||
WasmImpArgs(WasmImportFunc& e)
|
||||
{
|
||||
if constexpr (N < C)
|
||||
{
|
||||
using at = typename boost::mpl::at_c<mpl, N>::type;
|
||||
if constexpr (std::is_pointer_v<at>)
|
||||
e.params.push_back(WT_I32);
|
||||
else if constexpr (std::is_same_v<at, std::int32_t>)
|
||||
e.params.push_back(WT_I32);
|
||||
else if constexpr (std::is_same_v<at, std::int64_t>)
|
||||
e.params.push_back(WT_I64);
|
||||
else
|
||||
static_assert(std::is_pointer_v<at>, "Unsupported argument type");
|
||||
|
||||
return WasmImpArgs<N + 1, C, mpl>(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename rt>
|
||||
void
|
||||
WasmImpRet(WasmImportFunc& e)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<rt>)
|
||||
e.result = WT_I32;
|
||||
else if constexpr (std::is_same_v<rt, std::int32_t>)
|
||||
e.result = WT_I32;
|
||||
else if constexpr (std::is_same_v<rt, std::int64_t>)
|
||||
e.result = WT_I64;
|
||||
else if constexpr (std::is_void_v<rt>)
|
||||
e.result.reset();
|
||||
#if (defined(__GNUC__) && (__GNUC__ >= 14)) || ((defined(__clang_major__)) && (__clang_major__ >= 18))
|
||||
else
|
||||
static_assert(false, "Unsupported return type");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void
|
||||
WasmImpFuncHelper(WasmImportFunc& e)
|
||||
{
|
||||
using rt = typename bft::result_type<F>::type;
|
||||
using pt = typename bft::parameter_types<F>::type;
|
||||
// typename boost::mpl::at_c<mpl, N>::type
|
||||
|
||||
WasmImpRet<rt>(e);
|
||||
WasmImpArgs<0, bft::function_arity<F>::value, pt>(e);
|
||||
// WasmImpWrap(e, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void
|
||||
WasmImpFunc(ImportVec& v, std::string_view imp_name, void* f_wrap, void* data = nullptr, uint32_t gas = 0)
|
||||
{
|
||||
WasmImportFunc e;
|
||||
e.name = imp_name;
|
||||
e.wrap = f_wrap;
|
||||
e.gas = gas;
|
||||
WasmImpFuncHelper<F>(e);
|
||||
v.push_back(std::make_pair(data, std::move(e)));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct WasmParamVec
|
||||
{
|
||||
std::uint8_t const* d = nullptr;
|
||||
std::int32_t sz = 0;
|
||||
};
|
||||
|
||||
struct WasmParam
|
||||
{
|
||||
WasmTypes type = WT_I32;
|
||||
union
|
||||
{
|
||||
std::int32_t i32;
|
||||
std::int64_t i64 = 0;
|
||||
float f32;
|
||||
double f64;
|
||||
WasmParamVec u8v;
|
||||
} of;
|
||||
};
|
||||
|
||||
template <class... Types>
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v, std::int32_t p, Types&&... args)
|
||||
{
|
||||
v.push_back({.type = WT_I32, .of = {.i32 = p}});
|
||||
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v, std::int64_t p, Types&&... args)
|
||||
{
|
||||
v.push_back({.type = WT_I64, .of = {.i64 = p}});
|
||||
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
// We are not supporting float/double for now
|
||||
// Leaving this code here so that it is easier to add later if needed
|
||||
// template <class... Types>
|
||||
// inline void
|
||||
// wasmParamsHlp(std::vector<WasmParam>& v, float p, Types&&... args)
|
||||
// {
|
||||
// v.push_back({.type = WT_F32, .of = {.f32 = p}});
|
||||
// wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
// }
|
||||
|
||||
// template <class... Types>
|
||||
// inline void
|
||||
// wasmParamsHlp(std::vector<WasmParam>& v, double p, Types&&... args)
|
||||
// {
|
||||
// v.push_back({.type = WT_F64, .of = {.f64 = p}});
|
||||
// wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
// }
|
||||
|
||||
template <class... Types>
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v, std::uint8_t const* dt, std::int32_t sz, Types&&... args)
|
||||
{
|
||||
v.push_back({.type = WT_U8V, .of = {.u8v = {.d = dt, .sz = sz}}});
|
||||
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v, Bytes const& p, Types&&... args)
|
||||
{
|
||||
if (p.size() > std::numeric_limits<int32_t>::max())
|
||||
throw std::runtime_error("can't allocate memory, size: " + std::to_string(p.size())); // LCOV_EXCL_LINE
|
||||
|
||||
wasmParamsHlp(v, p.data(), static_cast<std::int32_t>(p.size()), std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v, std::string_view const& p, Types&&... args)
|
||||
{
|
||||
if (p.size() > std::numeric_limits<int32_t>::max())
|
||||
throw std::runtime_error("can't allocate memory, size: " + std::to_string(p.size()));
|
||||
|
||||
wasmParamsHlp(
|
||||
v,
|
||||
reinterpret_cast<std::uint8_t const*>(p.data()),
|
||||
static_cast<std::int32_t>(p.size()),
|
||||
std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v, std::string const& p, Types&&... args)
|
||||
{
|
||||
if (p.size() > std::numeric_limits<int32_t>::max())
|
||||
throw std::runtime_error("can't allocate memory, size: " + std::to_string(p.size())); // LCOV_EXCL_LINE
|
||||
|
||||
wasmParamsHlp(
|
||||
v,
|
||||
reinterpret_cast<std::uint8_t const*>(p.c_str()),
|
||||
static_cast<std::int32_t>(p.size()),
|
||||
std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline std::vector<WasmParam>
|
||||
wasmParams(Types&&... args)
|
||||
{
|
||||
std::vector<WasmParam> v;
|
||||
v.reserve(sizeof...(args));
|
||||
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, size_t size = sizeof(T)>
|
||||
inline constexpr T
|
||||
adjustWasmEndianessHlp(T x)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "Only integral types");
|
||||
if constexpr (size > 1)
|
||||
{
|
||||
using U = std::make_unsigned<T>::type;
|
||||
U u = static_cast<U>(x);
|
||||
U const low = (u & 0xFF) << ((size - 1) << 3);
|
||||
u = adjustWasmEndianessHlp<U, size - 1>(u >> 8);
|
||||
return static_cast<T>(low | u);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename T, size_t size = sizeof(T)>
|
||||
inline constexpr T
|
||||
adjustWasmEndianess(T x)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
static_assert(std::is_integral<T>::value, "Only integral types");
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
{
|
||||
return adjustWasmEndianessHlp(x);
|
||||
}
|
||||
return x;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user