Compare commits

...

228 Commits

Author SHA1 Message Date
Olek
d582ae7990 HF one entry point (#7393)
Add one entry point for all HF for centralized exceptions handling, gas calculation and general checks.
Add exception handling for HF
Add FieldLocator object
Switch pointers to references for HF and runtime
Max size for parameters and sfData field is 1 kb now
Fix Allhf unittest, to provide correct locator
2026-06-03 21:53:12 -04:00
Olek
0dbe51c740 Cleanup and some refactoring (#7383) 2026-06-02 21:15:58 -04:00
Olek
63fff4b518 Fix HF tests (#7365) 2026-05-29 17:49:03 -04:00
Mayukha Vadari
d85bf722ea fix: Fix build issues post-clang-tidy changes (#7298) 2026-05-20 13:44:18 -04:00
Mayukha Vadari
b664989cfb fix clang-tidy issues 2026-05-19 15:11:55 -04:00
Mayukha Vadari
e77934302a Merge branch 'ripple/wasmi' of https://github.com/XRPLF/rippled into ripple/wasmi-host-functions 2026-05-19 15:10:21 -04:00
Mayukha Vadari
ef7aeca6bf Merge branch 'develop' into ripple/wasmi 2026-05-18 18:25:09 -04:00
Denis Angell
ad3d172a1f fix: Use account ledger entry when canceling token escrows (#6171) 2026-05-18 20:08:48 +00:00
Mayukha Vadari
ad7232cbc5 refactor: Rename account_ to accountID_ (#7284) 2026-05-18 10:56:54 +00:00
Michael Legleux
93836f22db ci: Add Linux package builds (DEB + RPM) to CI (#6639) 2026-05-16 05:08:37 +00:00
Mayukha Vadari
c7ecfc6a97 refactor: Clean up comments post-clang-tidy changes (#7283) 2026-05-15 23:02:04 +00:00
Ed Hennis
6809690fad release: Set version to 3.3.0-b0 (#7280)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-05-15 18:04:49 +00:00
Mayukha Vadari
eec1d29b92 Merge branch 'develop' into ripple/wasmi 2026-05-15 11:36:56 -04:00
pwang200
971ba2281e clarify XLS-0102 host function stability rule (#7146) 2026-05-14 20:18:05 -04:00
pwang200
90357eeae1 bump get_nft host function cost from 1000 to 5000 (#7200) 2026-05-14 18:53:29 -04:00
Olek
597202a6f0 Refactoring float hostfunctions (#7053) 2026-05-07 12:33:22 -04:00
pwang200
1600b3e7f3 ai review nits fixes of host functions (#6963) 2026-04-30 13:56:55 -04:00
Mayukha Vadari
ecee732187 Merge branch 'develop' into ripple/wasmi 2026-04-22 17:22:28 -04:00
Olek
ce2586c039 Review fixes (#6512) 2026-04-20 14:03:39 -04:00
Olek
8cc2169939 test: Calling wrap functions from c++ side (#6699) 2026-04-09 18:48:58 -04:00
Mayukha Vadari
826f613ad8 Merge branch 'ripple/wasmi' of https://github.com/XRPLF/rippled into ripple/wasmi-host-functions 2026-04-08 13:51:09 -04:00
Mayukha Vadari
1259c1d5ca Merge branch 'develop' of https://github.com/XRPLF/rippled into ripple/wasmi 2026-04-08 13:48:41 -04:00
Olek
d2641d85bd New floats format, STAmount compatible (#6600) 2026-04-07 20:19:19 -04:00
Mayukha Vadari
75f66bd9fe fix build 2026-04-07 17:24:48 -04:00
Mayukha Vadari
7cd71cb659 Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-04-07 16:03:02 -04:00
Mayukha Vadari
9917f96166 Merge remote-tracking branch 'upstream/develop' into ripple/wasmi 2026-04-07 16:02:56 -04:00
Mayukha Vadari
e1cc82587b Merge branch 'ripple/wasmi' of https://github.com/XRPLF/rippled into ripple/wasmi-host-functions 2026-04-07 16:02:32 -04:00
Pratik Mankawde
2cc9439fde fix: Handle WSClient write failure when server closes WebSocket (#6671)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 16:01:26 -04:00
Ayaz Salikhov
52af9582e2 ci: Change conditions for uploading artifacts in public/private/org repos (#6734) 2026-04-07 16:01:26 -04:00
Bart
46e88dc732 refactor: Rename non-functional uses of ripple(d) to xrpl(d) (#6676)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-04-07 16:01:26 -04:00
Mayukha Vadari
bc24f2e211 refactor: Move more helper files into libxrpl/ledger/helpers (#6731)
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-04-07 16:01:26 -04:00
Mayukha Vadari
7a7c993b15 fix: Minor RPC fixes (#6730) 2026-04-07 16:01:26 -04:00
Zhiyuan Wang
9733ca8f91 fix: Prevent deletion of MPTokens with active escrow (#6635)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-04-07 16:01:26 -04:00
Vito Tumas
18d5e3e226 fix: Clamp VaultClawback to assetsAvailable for zero-amount clawback (#6646) 2026-04-07 16:01:25 -04:00
Vito Tumas
b30b4e1d65 fix: Add assorted Lending Protocol fixes (#6678)
Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
2026-04-07 16:01:25 -04:00
Mayukha Vadari
d435893602 fix: Change variable signedness and correctly handle std::optional (#6657) 2026-04-07 16:01:25 -04:00
Olek
00b0cf50f6 Update wasmi to 1.0.9 (#6727) 2026-04-07 15:58:29 -04:00
Mayukha Vadari
7ef256499c Merge branch 'ripple/wasmi' of https://github.com/XRPLF/rippled into wasmi-host-functions 2026-04-03 09:57:07 -04:00
Mayukha Vadari
1338062be7 Merge branch 'develop' of https://github.com/XRPLF/rippled into ripple/wasmi 2026-04-03 09:56:55 -04:00
Mayukha Vadari
4fc1778ec8 fix clang-tidy issues 2026-04-03 09:56:42 -04:00
Oleksandr
65322d9e78 fix Clang-tidy 2026-04-02 21:33:43 -04:00
Mayukha Vadari
c5598a4284 fix clang-tidy issues 2026-04-02 19:05:34 -04:00
Mayukha Vadari
0deb6bcadf fix build 2026-04-02 18:39:14 -04:00
Mayukha Vadari
9b013b559b Merge branch 'ripple/wasmi' of https://github.com/XRPLF/rippled into wasmi-host-functions 2026-04-02 17:54:32 -04:00
Mayukha Vadari
1d4a3c00b8 Merge branch 'develop' of https://github.com/XRPLF/rippled into ripple/wasmi 2026-04-02 17:53:53 -04:00
Mayukha Vadari
4b34102e8e test: Use proper length limits in codecov_tests (#6626) 2026-03-25 09:10:12 -07:00
Olek
d006433579 Base divison of large fixtures (#6637) 2026-03-25 09:42:33 -04:00
Mayukha Vadari
a7ab8ee923 clang-tidy fixes 2026-03-24 10:22:01 -07:00
Mayukha Vadari
e0073a4402 Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-03-24 10:20:43 -07:00
Mayukha Vadari
2930ef217f Merge remote-tracking branch 'upstream/develop' into ripple/wasmi 2026-03-24 10:20:38 -07:00
Mayukha Vadari
9dbb301699 more clang-tidy fixes 2026-03-24 10:20:06 -07:00
Mayukha Vadari
531e8b6ebd fix clang-tidy 2026-03-24 09:46:01 -07:00
Mayukha Vadari
90397e1a52 more build fixes 2026-03-24 09:41:08 -07:00
Mayukha Vadari
888ca2e6d9 fix build 2026-03-24 09:29:05 -07:00
Mayukha Vadari
b6514b680f Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-03-24 08:47:12 -07:00
Mayukha Vadari
913e4b919e Merge remote-tracking branch 'upstream/develop' into ripple/wasmi 2026-03-24 08:41:12 -07:00
Olek
196e6a1b27 Clang-format fixtures.cpp (#6610) 2026-03-20 14:26:26 -04:00
Olek
27468ddbcf Add import / export sections test (#6497) 2026-03-19 12:46:58 -04:00
Mayukha Vadari
bce5d91e45 Merge branch 'develop' into ripple/wasmi 2026-03-12 14:37:01 -04:00
Mayukha Vadari
654338fa66 Merge branch 'develop' into ripple/wasmi 2026-03-06 16:27:50 -04:00
Mayukha Vadari
9c25d18851 Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-03-05 13:48:33 -04:00
Mayukha Vadari
3a825a41e1 Merge branch 'develop' into ripple/wasmi 2026-03-05 13:48:16 -04:00
Jingchen
a9ebf786c6 Modularise wasm (#6441)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2026-03-04 20:21:51 +00:00
Olek
5afe8cc321 Fix clang tidy (#6463)
* Fix clang tidy

* Add exponent overflow test
2026-03-04 11:30:33 -05:00
Mayukha Vadari
bc5ec3c962 assorted fixes (#6376) 2026-03-04 09:30:09 -04:00
Mayukha Vadari
1775251e90 Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-03-03 11:18:41 -04:00
Mayukha Vadari
61bcb7621f Merge branch 'develop' into ripple/wasmi 2026-03-03 11:18:26 -04:00
Mayukha Vadari
a3f71b1774 Merge branch 'develop' into ripple/wasmi 2026-03-02 17:06:17 -05:00
Mayukha Vadari
4df7d1a4bb rename variable 2026-03-02 16:48:02 -04:00
Mayukha Vadari
125df7a425 Merge remote-tracking branch 'upstream/ripple/wasmi' into wasmi-host-functions 2026-02-27 16:46:43 -05:00
Mayukha Vadari
b08bcf5d21 Merge branch 'develop' into ripple/wasmi 2026-02-27 16:41:44 -05:00
Mayukha Vadari
dc413aef0c Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-02-27 16:28:34 -05:00
Mayukha Vadari
77dfd56ace Merge branch 'develop' into ripple/wasmi 2026-02-27 13:49:25 -05:00
Olek
953b9a3500 Disable reusing wasm module (#6364)
* Remove ability to re-use wasm module

* Check that HFS object is always new

* Fix clang format

* Remove perf tests

* temp build fix

* Fix merge
2026-02-26 15:30:46 -05:00
Olek
1d9ec84350 Test invalid opcodes (#6392) 2026-02-26 09:59:30 -05:00
Olek
0392846a17 UT for wasm parameters (#6413) 2026-02-25 11:49:27 -05:00
Mayukha Vadari
1b4a564369 fix build issues 2026-02-18 13:20:29 -05:00
Mayukha Vadari
fd524c4be9 fix pre-commit 2026-02-18 12:41:56 -05:00
Mayukha Vadari
495dda7f58 Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-02-18 12:36:07 -05:00
Mayukha Vadari
9c3c0280b1 Merge branch 'develop' into ripple/wasmi 2026-02-18 12:35:51 -05:00
Mayukha Vadari
f73d8a6cf2 clean up some hf code (#6354)
* clean up some hf code

* fix comments

* fix ubsan

* Revert "fix ubsan"
2026-02-13 11:27:50 -05:00
Olek
6728ab52b7 Add tests for wasm functions with many parameters (#6343)
* Add functions with many parameters

* Add 10k locals function

* Module with  5k functions

* fix typo

Co-authored-by: Mayukha Vadari <mvadari@gmail.com>

---------

Co-authored-by: Mayukha Vadari <mvadari@gmail.com>
2026-02-10 18:10:33 -05:00
Mayukha Vadari
77673663ca fix cspell issues in tests (#6348) 2026-02-10 17:42:41 -05:00
Mayukha Vadari
c1381f8ddd Merge branch 'ripple/wasmi' into wasmi-host-functions 2026-02-10 17:27:18 -05:00
Mayukha Vadari
bd16f7989d Merge branch 'develop' into ripple/wasmi 2026-02-10 17:26:33 -05:00
Mayukha Vadari
65f9cf80c0 add readme to src/xrpld/app/wasm (#6340)
* add readme to src/xrpl/app/wasm

* important block

* respond to copilot
2026-02-09 12:13:39 -05:00
Mayukha Vadari
de55a5ebfc Merge branch 'ripple/wasmi' into wasmi-host-functions 2026-02-04 18:13:15 -05:00
Mayukha Vadari
2ec4a1114e Merge branch 'develop' into ripple/wasmi 2026-02-04 18:13:00 -05:00
Olek
ba03a8a9d2 Fix negation of int64_t (#6296) 2026-02-03 17:43:54 -05:00
Mayukha Vadari
7c8279ec83 use buffers for uint32 WASM params (#6291) 2026-02-03 16:08:46 -05:00
Mayukha Vadari
0418ffb26a Merge branch 'ripple/wasmi' into wasmi-host-functions 2026-02-03 14:52:16 -05:00
Mayukha Vadari
b2627039f6 Merge branch 'develop' into ripple/wasmi 2026-02-03 14:51:59 -05:00
Mayukha Vadari
8f97ec3bde Merge branch 'ripple/wasmi' into wasmi-host-functions 2026-01-29 13:54:30 -05:00
Mayukha Vadari
e85e7b1b1a Merge branch 'develop' into ripple/wasmi 2026-01-29 13:53:55 -05:00
Mayukha Vadari
803a344c65 fix clang-format 2026-01-28 16:35:02 -05:00
Mayukha Vadari
4eb34f381a Merge branch 'ripple/wasmi' into wasmi-host-functions 2026-01-28 15:56:40 -05:00
Mayukha Vadari
72fffb6e51 Merge branch 'develop' into ripple/wasmi 2026-01-28 15:56:18 -05:00
Mayukha Vadari
f7ee580f01 Merge commit '5f638f55536def0d88b970d1018a465a238e55f4' into ripple/wasmi 2026-01-28 15:56:11 -05:00
Mayukha Vadari
122d405750 Merge commit '92046785d1fea5f9efe5a770d636792ea6cab78b' into ripple/wasmi 2026-01-28 15:56:04 -05:00
Olek
c1c1b4ea67 Reject non-canonical binaries (#6277)
* Reject non-canonical binaries

* Review fixes

* Cleanup Number2 class

* Use enum instead of 0
2026-01-27 16:30:51 -05:00
Mayukha Vadari
977caea0a5 Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-01-27 13:26:55 -05:00
Mayukha Vadari
d7ed6d6512 Merge branch 'develop' into ripple/wasmi 2026-01-27 13:26:39 -05:00
Olek
f1f2e2629f Fix for Big-Endian machines (#6245) 2026-01-27 13:05:54 -05:00
Olek
917c610f96 Ensure request size less than int limit (#6239)
* Ensure request size less than int limit

* Move size check to wasmParams function
2026-01-27 12:37:47 -05:00
Mayukha Vadari
317e533d81 clean up Wasm_test.cpp more (#6278) 2026-01-26 15:21:15 -05:00
Olek
4160677878 Switch to series expansion method for ln() (#6268)
* Switch to series expansion method for ln()
Add float lg() tests to Number tests;
* Rename lg -> log10
* Add check for 0 to log10()
2026-01-26 14:04:03 -05:00
Olek
df98db1452 Check wasm return type (#6240)
* Check wasm return type

* Add more tests
2026-01-23 16:12:14 -05:00
Mayukha Vadari
673476ef1b Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-01-23 13:13:26 -05:00
Mayukha Vadari
8bc6f9cd70 Merge branch 'develop' into ripple/wasmi 2026-01-23 13:13:11 -05:00
Mayukha Vadari
ba5debfecd update return calculation (#6250) 2026-01-22 17:01:56 -05:00
Mayukha Vadari
f4a27c9b6d minor refactor of Wasm_test (#6229) 2026-01-21 18:05:48 -05:00
Olek
fd1cb318e3 Check that max parameters length is multiple of sizeof(int32) (#6253) 2026-01-21 17:22:47 -05:00
Mayukha Vadari
8c3544a58c Merge branch 'ripple/wasmi' into wasmi-host-functions 2026-01-21 12:57:47 -05:00
Mayukha Vadari
ed5139d4e3 Merge branch 'develop' into ripple/wasmi 2026-01-21 12:57:29 -05:00
Olek
42494dd4cf Ensure lifetime of imports (#6230) 2026-01-21 12:43:12 -05:00
Mayukha Vadari
ce84cc8b44 improve trace hf code (#6190)
* adjust trace statements

* add helper function

* use lambda instead

* use same paradigm in TestHostFunctions

* oops
2026-01-15 20:50:55 -05:00
Mayukha Vadari
9a9a7aab01 Add Vector256 support to the locator (#6131)
* add Vector256 nesting/length support

* [WIP] add tests

* fix tests

* simplify with helper function

* oops typo

* remove static variable

* respond to comments

* STBaseOrUInt256->FieldValue

* oops

* add more tests for coverage

* respond to comments
2026-01-15 20:14:42 -05:00
Olek
209a1a6ffa Don't throw from hostfunctions stack (#6221) 2026-01-15 19:52:22 -05:00
Oleksandr
fc35a9f9c8 Fix usage of the Number class 2026-01-14 19:36:50 -05:00
Oleksandr
c5e50aa221 Fix merge issues 2026-01-14 14:46:35 -05:00
Mayukha Vadari
074b1f00d5 Merge branch 'ripple/wasmi' into wasmi-host-functions 2026-01-14 13:04:28 -05:00
Mayukha Vadari
7a9d245950 Merge branch 'develop' into ripple/wasmi 2026-01-14 13:01:35 -05:00
Mayukha Vadari
1809fe07f2 remove test file 2026-01-14 12:43:12 -05:00
Mayukha Vadari
409c67494a move helper functions to separate file (#6178)
* move helper functions to separate file

* break it up into sections, split out float helpers

* split impls into multiple cpp files

* namespace detail

* fix build issue

* fix tests

* clean up

* put float helpers into wasm_float namespace
2026-01-13 20:34:57 -05:00
Olek
c626b6403a Fix unaligned access (#6208) 2026-01-13 16:40:42 -05:00
Olek
81cbc91927 Fix traces (#6127)
* Fix traces
* More tests for codecov
* Review fixes
* trace float test
* Fix return value for traces
* Remove SuiteJournalSink2
* Add explicit severity
* Move logs to ApplyView
* Add check for output strings
* Merging fix
2026-01-13 16:38:48 -05:00
pwang200
1c812a6c4d disable Wasm features added in Wasmi 1.0, and fix unit test fuel cost due to Wasmi 1.0 fuel changes (#6173)
* disable 4 more wasm features

* unit tests for disabled Wasmi 1.0 features

* fix unit tests failed due to fuel changes

* rearrange wasm feature unit tests

* fix gas costs

* Update src/test/app/wasm_fixtures/wat/custom_page_sizes.wat

---------

Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
2026-01-12 22:04:33 -05:00
Mayukha Vadari
0724927799 Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-01-12 15:17:36 -05:00
Olek
d83ec96848 Switch to wasmi v1.0.6 (#6204) 2026-01-12 13:36:02 -05:00
Mayukha Vadari
375dd50b35 Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-01-12 13:19:17 -05:00
Mayukha Vadari
419d53ec4c Merge branch 'develop' into ripple/wasmi 2026-01-12 13:10:58 -05:00
Mayukha Vadari
d4d70d5675 Merge branch 'develop' into ripple/wasmi 2026-01-12 12:27:48 -05:00
Olek
6ab15f8377 Add checks to allocate (#6185) 2026-01-09 14:49:09 -05:00
pwang200
91f3d51f3d fix start function loop 2026-01-09 11:38:54 -05:00
pwang200
9ed60b45f8 section corruption unit tests 2026-01-08 16:15:36 -05:00
pwang200
d5c53dcfd2 fix Uninitialized import entries lead to undefined behavior During WASM Instantiation 2026-01-08 16:14:49 -05:00
Mayukha Vadari
e94321fb41 Merge branch 'ripple/wasmi' into wasmi-host-functions 2026-01-08 11:44:15 -05:00
Mayukha Vadari
bbc28b3b1c Merge branch 'develop' into ripple/wasmi 2026-01-08 11:42:28 -05:00
Mayukha Vadari
843e981c8a Merge remote-tracking branch 'upstream/ripple/wasmi' into wasmi-host-functions 2026-01-07 16:52:56 -05:00
Mayukha Vadari
5aab274b7a Merge branch 'develop' into ripple/wasmi 2026-01-07 16:52:10 -05:00
Mayukha Vadari
2c30e41191 use the develop hashes 2026-01-07 16:50:45 -05:00
Mayukha Vadari
8ea5106b0b Merge branch 'develop' into ripple/wasmi 2026-01-07 14:34:49 -05:00
Mayukha Vadari
f57f67a8ae infinite loop test (#6064) 2026-01-07 11:51:58 -05:00
pwang200
a98269f049 a batch of memory, table, and trap tests (#6100)
wasm memory, table, and trap unit tests
2026-01-06 14:03:18 -05:00
Mayukha Vadari
b66bc47ca9 fix more merge issues 2026-01-06 13:30:30 -05:00
Mayukha Vadari
0e9c7458bb fix more merge issues 2026-01-05 18:53:14 -05:00
Mayukha Vadari
1d89940653 merge fixes 2026-01-05 18:48:09 -05:00
Mayukha Vadari
1a1a6806ec Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2026-01-05 18:44:41 -05:00
Mayukha Vadari
1977df9c2e Merge remote-tracking branch 'upstream/develop' into ripple/wasmi 2026-01-05 18:43:49 -05:00
Mayukha Vadari
6c95548df5 Merge remote-tracking branch 'upstream/develop' into ripple/wasmi 2025-12-22 15:51:19 -08:00
Olek
69ab39d658 Fix potential memory leaks found by srlabs (#6145) 2025-12-18 14:13:48 -05:00
Mayukha Vadari
b9eb66eecc fix parameter index desynchronization (#6148) 2025-12-17 14:19:34 -08:00
Mayukha Vadari
881087dd3d Merge remote-tracking branch 'upstream/ripple/wasmi' into wasmi-host-functions 2025-12-08 14:29:47 -05:00
Mayukha Vadari
90e0bbd0fc Merge branch 'develop' into ripple/wasmi 2025-12-08 14:28:41 -05:00
Olek
b57df290de Use conan repo for wasmi lib (#6109)
* Use conan repo for wasmi lib
* Generate lockfile
2025-12-08 13:02:01 -05:00
Mayukha Vadari
8a403f1241 Merge branch 'develop' into ripple/wasmi 2025-12-05 14:32:48 -05:00
Mayukha Vadari
6d2640871d Merge branch 'develop' into ripple/wasmi 2025-12-02 18:40:54 -05:00
pwang200
c145598ff9 add memory limit and disable float and other advanced instructions 2025-12-02 00:09:20 -05:00
Olek
50e5608d86 wasmi HF cost 2025-12-01 20:21:52 -05:00
Mayukha Vadari
7a7b96107c Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions 2025-11-25 03:42:05 +05:30
Olek
500bb68831 Fix win build (#6076) 2025-11-24 16:56:23 -05:00
Mayukha Vadari
53eb0f60bc fix another build issue 2025-11-25 03:10:58 +05:30
Mayukha Vadari
41205ae928 Merge branch 'ripple/wasmi' into wasmi-host-functions 2025-11-25 03:01:51 +05:30
Mayukha Vadari
c33b0ae463 fix build issue 2025-11-25 02:58:57 +05:30
Mayukha Vadari
16087c9680 fix merge issue 2025-11-25 02:57:47 +05:30
Mayukha Vadari
56bc6d58f6 Merge branch 'ripple/wasmi' into wasmi-host-functions 2025-11-25 02:45:00 +05:30
Mayukha Vadari
ef5d335e09 update 2025-11-25 02:44:18 +05:30
Mayukha Vadari
25c3060fef remove conan.lock (temporary) 2025-11-25 02:40:57 +05:30
Mayukha Vadari
ce9f0b38a4 Merge branch 'develop' into ripple/wasmi 2025-11-25 02:33:47 +05:30
Mayukha Vadari
35f7cbf772 update 2025-11-25 02:31:51 +05:30
Mayukha Vadari
0db564d261 WASMI data 2025-11-04 15:57:07 -05:00
Mayukha Vadari
427b7ea104 run rename script 2025-11-04 15:29:08 -05:00
Mayukha Vadari
7bf6878b4b fix imports 2025-11-04 14:49:45 -05:00
Mayukha Vadari
0bc1a115ff Merge branch 'wamr' into wamr-host-functions 2025-11-04 13:36:22 -05:00
Mayukha Vadari
334bcfa5ef Merge branch 'develop' into wamr 2025-11-04 13:36:01 -05:00
Mayukha Vadari
106dea4559 update fixtures to use the latest version of stdlib 2025-11-04 13:35:25 -05:00
Mayukha Vadari
3ffdcf8114 allow 0-value trace amounts 2025-11-04 13:19:40 -05:00
Olek
4021a7eb28 Wamr and HF security review fixes (#5965) 2025-10-31 10:34:31 -04:00
Ayaz Salikhov
0690fda0f1 Merge branch 'develop' into ripple/wamr 2025-10-30 14:12:15 +00:00
Mayukha Vadari
d0cc48c6d3 Update cmake/RippledCore.cmake
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
2025-10-29 16:41:11 -04:00
Olek
d66e3c949e Chores: Sort package list (#5963) 2025-10-29 12:55:07 -04:00
Mayukha Vadari
0c65a386b5 fix tests 2025-10-24 18:01:01 -04:00
Mayukha Vadari
29f5430881 fix bug 2025-10-24 16:05:38 -04:00
Mayukha Vadari
101f285bcd return size from updateData 2025-10-24 16:01:45 -04:00
Mayukha Vadari
286dc6322b Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-10-23 15:38:28 -04:00
Mayukha Vadari
c9346cd40d Merge branch 'develop' into ripple/wamr 2025-10-23 15:38:04 -04:00
Mayukha Vadari
1c5683ec78 Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-10-20 11:53:22 -04:00
Mayukha Vadari
9bee155d59 Merge branch 'develop' into ripple/wamr 2025-10-20 11:53:03 -04:00
Mayukha Vadari
f34b05f4de Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-10-16 12:12:05 -04:00
Mayukha Vadari
97ce25f4ce Merge branch 'develop' into ripple/wamr 2025-10-16 12:11:55 -04:00
Olek
9e14c14a26 Use xrplf conan repo for wamr (#5862) 2025-10-13 15:11:21 -04:00
Mayukha Vadari
c507880d8f Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-10-13 13:57:22 -04:00
Mayukha Vadari
3f8328bbf8 Merge branch 'develop' into ripple/wamr 2025-10-13 13:55:07 -04:00
Mayukha Vadari
c10a5f9ef6 Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-10-09 17:10:31 -04:00
Mayukha Vadari
3c141de695 Merge branch 'develop' into ripple/wamr 2025-10-09 16:52:25 -04:00
Mayukha Vadari
da2b9455f2 fix: remove get_ledger_account_hash and get_ledger_tx_hash host functions (#5850)
* remove `get_ledger_account_hash` and `get_ledger_tx_hash`

* fix build+tests
2025-10-06 16:38:40 -04:00
Mayukha Vadari
cb622488c0 Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-10-02 14:35:25 -04:00
Mayukha Vadari
32f971fec6 Merge branch 'develop' into ripple/wamr 2025-10-02 14:35:13 -04:00
Mayukha Vadari
8dea76baa4 Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-09-30 14:42:49 -04:00
Mayukha Vadari
299fbe04c4 Merge branch 'develop' into ripple/wamr 2025-09-30 14:42:24 -04:00
Mayukha Vadari
57fc1df7d7 switch from wasm32-unknown-unknown to wasm32v1-none (#5814) 2025-09-29 15:43:22 -04:00
Mayukha Vadari
eaba76f9e6 Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-09-26 16:37:25 -04:00
Mayukha Vadari
cb702cc238 Merge branch 'develop' into ripple/wamr 2025-09-26 16:37:04 -04:00
Mayukha Vadari
b69b4a0a4a Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-09-26 15:51:48 -04:00
Mayukha Vadari
50d6072a73 Merge branch 'develop' into ripple/wamr 2025-09-26 15:51:40 -04:00
Olek
d24cd50e61 Switch to own wamr fork (#5808) 2025-09-23 16:39:21 -04:00
Mayukha Vadari
9f5875158c Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-09-22 18:23:45 -04:00
Mayukha Vadari
c3dc33c861 Merge branch 'develop' into ripple/wamr 2025-09-22 18:23:35 -04:00
Olek
6be8f2124c Latests HF perf test (#5789) 2025-09-18 15:51:39 -04:00
Mayukha Vadari
edfed06001 fix merge issues 2025-09-18 15:39:49 -04:00
Mayukha Vadari
1c646dba91 Merge remote-tracking branch 'upstream/ripple/wamr' into wamr-host-functions 2025-09-18 15:29:02 -04:00
Mayukha Vadari
6781068058 Merge branch 'develop' into ripple/wamr 2025-09-18 15:27:54 -04:00
Mayukha Vadari
cfe57c1dfe Merge branch 'ripple/wamr' into ripple/wamr-host-functions 2025-09-18 14:37:58 -04:00
Mayukha Vadari
c34d09a971 Merge branch 'develop' into ripple/wamr 2025-09-18 14:24:34 -04:00
Mayukha Vadari
ebd90c4742 chore: remove unneeded float stuff (#5729) 2025-09-11 18:41:24 -04:00
Mayukha Vadari
ba52d34828 test: improve codecov in HostFuncWrapper.cpp (#5730) 2025-09-11 18:09:08 -04:00
Mayukha Vadari
1b6312afb3 rearrange files 2025-09-11 16:34:03 -04:00
Mayukha Vadari
bf32dc2e72 add fixtures files 2025-09-11 16:28:11 -04:00
Mayukha Vadari
a15d65f7a2 update tests 2025-09-11 16:20:33 -04:00
Mayukha Vadari
2de8488855 add temBAD_WASM 2025-09-11 16:02:17 -04:00
Mayukha Vadari
129aa4bfaa bring out IOUAmount.h 2025-09-11 13:18:42 -04:00
Mayukha Vadari
b1d70db63b limits 2025-09-10 15:05:06 -04:00
Mayukha Vadari
f03c3aafe4 misc host function files 2025-09-10 15:02:48 -04:00
Mayukha Vadari
51a9f106d1 CODEOWNERS 2025-09-10 14:59:09 -04:00
Mayukha Vadari
bfc048e3fe add tests 2025-09-10 14:57:23 -04:00
Mayukha Vadari
83418644f7 add host functions 2025-09-10 14:56:21 -04:00
Mayukha Vadari
dbc9dd5bfc Add WAMR integration code 2025-09-10 14:56:08 -04:00
Mayukha Vadari
45ab15d4b5 add WAMR dependency 2025-09-10 14:40:48 -04:00
188 changed files with 28799 additions and 300 deletions

View File

@@ -32,7 +32,32 @@ We will further set additional CMake arguments as follows:
"""
def generate_strategy_matrix(all: bool, config: Config) -> list:
def build_config_name(os_entry: dict[str, str], platform: str, build_type: str) -> str:
parts = [os_entry["distro_name"]]
for key in ("distro_version", "compiler_name", "compiler_version"):
if value := os_entry[key]:
parts.append(value)
parts.append("arm64" if "arm64" in platform else "amd64")
parts.append(build_type.lower())
return "-".join(parts)
def generate_packaging_matrix(config: Config) -> list[dict]:
"""Emit one entry per os entry with `package: true`. Architecture is
hardcoded to linux/amd64 here (and the runner is hardcoded at the
workflow level) until arm64 packaging is ready.
"""
return [
{
"artifact_name": f"xrpld-{build_config_name(os, 'linux/amd64', 'Release')}",
"os": os,
}
for os in config.os
if os.get("package", False)
]
def generate_strategy_matrix(all: bool, config: Config) -> list[dict]:
configurations = []
for architecture, os, build_type, cmake_args in itertools.product(
config.architecture, config.os, config.build_type, config.cmake_args
@@ -101,14 +126,15 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
continue
# RHEL:
# - 9 using GCC 12: Debug on linux/amd64.
# - 9 using GCC 12: Debug and Release on linux/amd64
# (Release is required for RPM packaging).
# - 10 using Clang: Release on linux/amd64.
if os["distro_name"] == "rhel":
skip = True
if os["distro_version"] == "9":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
and build_type == "Debug"
and build_type in ["Debug", "Release"]
and architecture["platform"] == "linux/amd64"
):
skip = False
@@ -123,7 +149,8 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
continue
# Ubuntu:
# - Jammy using GCC 12: Debug on linux/arm64.
# - Jammy using GCC 12: Debug on linux/arm64, Release on
# linux/amd64 (Release is required for DEB packaging).
# - Noble using GCC 14: Release on linux/amd64.
# - Noble using Clang 18: Debug on linux/amd64.
# - Noble using Clang 19: Release on linux/arm64.
@@ -136,6 +163,12 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
and architecture["platform"] == "linux/arm64"
):
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
skip = False
elif os["distro_version"] == "noble":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-14"
@@ -218,17 +251,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
# Generate a unique name for the configuration, e.g. macos-arm64-debug
# or debian-bookworm-gcc-12-amd64-release.
config_name = os["distro_name"]
if (n := os["distro_version"]) != "":
config_name += f"-{n}"
if (n := os["compiler_name"]) != "":
config_name += f"-{n}"
if (n := os["compiler_version"]) != "":
config_name += f"-{n}"
config_name += (
f"-{architecture['platform'][architecture['platform'].find('/')+1:]}"
)
config_name += f"-{build_type.lower()}"
config_name = build_config_name(os, architecture["platform"], build_type)
if "-Dcoverage=ON" in cmake_args:
config_name += "-coverage"
if "-Dunity=ON" in cmake_args:
@@ -332,10 +355,19 @@ if __name__ == "__main__":
required=False,
type=Path,
)
parser.add_argument(
"-p",
"--packaging",
help="Emit the packaging matrix (derived from the 'package' field on os entries) instead of the build/test matrix.",
action="store_true",
)
args = parser.parse_args()
matrix = []
if args.config is None or args.config == "":
if args.packaging:
config_path = args.config if args.config else THIS_DIR / "linux.json"
matrix += generate_packaging_matrix(read_config(config_path))
elif args.config is None or args.config == "":
matrix += generate_strategy_matrix(
args.all, read_config(THIS_DIR / "linux.json")
)

View File

@@ -127,7 +127,8 @@
"distro_version": "9",
"compiler_name": "gcc",
"compiler_version": "12",
"image_sha": "4c086b9"
"image_sha": "4c086b9",
"package": true
},
{
"distro_name": "rhel",
@@ -169,7 +170,8 @@
"distro_version": "jammy",
"compiler_name": "gcc",
"compiler_version": "12",
"image_sha": "4c086b9"
"image_sha": "4c086b9",
"package": true
},
{
"distro_name": "ubuntu",

View File

@@ -64,11 +64,13 @@ jobs:
.github/workflows/reusable-build-test-config.yml
.github/workflows/reusable-build-test.yml
.github/workflows/reusable-clang-tidy.yml
.github/workflows/reusable-package.yml
.github/workflows/reusable-strategy-matrix.yml
.github/workflows/reusable-test.yml
.github/workflows/reusable-upload-recipe.yml
.clang-tidy
.codecov.yml
cfg/**
cmake/**
conan/**
external/**
@@ -78,6 +80,10 @@ jobs:
CMakeLists.txt
conanfile.py
conan.lock
LICENSE.md
package/**
README.md
- name: Check whether to run
# This step determines whether the rest of the workflow should
# run. The rest of the workflow will run if this job runs AND at
@@ -134,6 +140,11 @@ jobs:
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
package:
needs: [should-run, build-test]
if: ${{ needs.should-run.outputs.go == 'true' }}
uses: ./.github/workflows/reusable-package.yml
upload-recipe:
needs:
- should-run
@@ -168,6 +179,7 @@ jobs:
- check-rename
- clang-tidy
- build-test
- package
- upload-recipe
- notify-clio
runs-on: ubuntu-latest

View File

@@ -1,5 +1,5 @@
# This workflow uploads the libxrpl recipe to the Conan remote when a versioned
# tag is pushed.
# This workflow uploads the libxrpl recipe to the Conan remote and builds
# release packages when a versioned tag is pushed.
name: Tag
on:
@@ -22,3 +22,22 @@ jobs:
secrets:
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
build-test:
if: ${{ github.repository == 'XRPLF/rippled' }}
uses: ./.github/workflows/reusable-build-test.yml
strategy:
fail-fast: true
matrix:
os: [linux]
with:
ccache_enabled: false
os: ${{ matrix.os }}
strategy_matrix: minimal
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
package:
if: ${{ github.repository == 'XRPLF/rippled' }}
needs: build-test
uses: ./.github/workflows/reusable-package.yml

View File

@@ -21,11 +21,13 @@ on:
- ".github/workflows/reusable-build-test-config.yml"
- ".github/workflows/reusable-build-test.yml"
- ".github/workflows/reusable-clang-tidy.yml"
- ".github/workflows/reusable-package.yml"
- ".github/workflows/reusable-strategy-matrix.yml"
- ".github/workflows/reusable-test.yml"
- ".github/workflows/reusable-upload-recipe.yml"
- ".clang-tidy"
- ".codecov.yml"
- "cfg/**"
- "cmake/**"
- "conan/**"
- "external/**"
@@ -35,6 +37,9 @@ on:
- "CMakeLists.txt"
- "conanfile.py"
- "conan.lock"
- "LICENSE.md"
- "package/**"
- "README.md"
# Run at 06:32 UTC on every day of the week from Monday through Friday. This
# will force all dependencies to be rebuilt, which is useful to verify that
@@ -95,3 +100,7 @@ jobs:
secrets:
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
package:
needs: build-test
uses: ./.github/workflows/reusable-package.yml

99
.github/workflows/reusable-package.yml vendored Normal file
View File

@@ -0,0 +1,99 @@
# Build Linux packages (DEB and RPM) from pre-built binary artifacts.
# Discovers which configurations to package from linux.json (os entries
# with "package": true) and fans out one job per entry. Today only
# linux/amd64 is emitted; the architecture is hardcoded both here
# (runner) and in generate.py.
name: Package
on:
workflow_call:
inputs:
pkg_release:
description: "Package release number. Increment when repackaging the same executable."
required: false
type: string
default: "1"
defaults:
run:
shell: bash
env:
BUILD_DIR: build
jobs:
generate-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.13
- name: Generate packaging matrix
id: generate
working-directory: .github/scripts/strategy-matrix
run: |
./generate.py --packaging --config=linux.json >> "${GITHUB_OUTPUT}"
generate-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: |
.github/actions/generate-version
src/libxrpl/protocol/BuildInfo.cpp
- name: Generate version
id: version
uses: ./.github/actions/generate-version
package:
needs: [generate-matrix, generate-version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
name: "${{ matrix.artifact_name }}"
permissions:
contents: read
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: ${{ format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) }}
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download pre-built binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ matrix.artifact_name }}
path: ${{ env.BUILD_DIR }}
- name: Make binary executable
run: chmod +x "${BUILD_DIR}/xrpld"
- name: Build package
env:
PKG_VERSION: ${{ needs.generate-version.outputs.version }}
PKG_RELEASE: ${{ inputs.pkg_release }}
run: ./package/build_pkg.sh
- name: Upload package artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: ${{ github.event.repository.visibility == 'public' }}
with:
name: ${{ matrix.artifact_name }}-pkg-${{ needs.generate-version.outputs.version }}
path: |
${{ env.BUILD_DIR }}/debbuild/*.deb
${{ env.BUILD_DIR }}/debbuild/*.ddeb
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/*.rpm
if-no-files-found: error

View File

@@ -93,6 +93,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(
@@ -134,6 +135,7 @@ endif()
include(XrplCore)
include(XrplProtocolAutogen)
include(XrplInstall)
include(XrplPackaging)
include(XrplValidatorKeys)
if(tests)

View File

@@ -28,7 +28,7 @@
# https://vl.ripple.com
# https://unl.xrplf.org
# http://127.0.0.1:8000
# file:///etc/opt/xrpld/vl.txt
# file:///etc/xrpld/vl.txt
#
# [validator_list_keys]
#

View File

@@ -67,6 +67,7 @@ target_link_libraries(
Xrpl::opts
Xrpl::syslibs
secp256k1::secp256k1
wasmi::wasmi
xrpl.libpb
xxHash::xxhash
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>

44
cmake/XrplPackaging.cmake Normal file
View File

@@ -0,0 +1,44 @@
#[===================================================================[
Linux packaging support: 'package' target.
The packaging script (package/build_pkg.sh) installs to FHS-standard
paths (/usr/bin, /etc/xrpld, etc.) regardless of CMAKE_INSTALL_PREFIX,
so no prefix guard is needed here.
#]===================================================================]
if(NOT is_linux)
message(STATUS "Packaging not supported on non-Linux hosts")
return()
endif()
if(NOT DEFINED pkg_release)
set(pkg_release 1)
endif()
find_program(RPMBUILD_EXECUTABLE rpmbuild)
find_program(DPKG_BUILDPACKAGE_EXECUTABLE dpkg-buildpackage)
if(NOT (RPMBUILD_EXECUTABLE OR DPKG_BUILDPACKAGE_EXECUTABLE))
message(
STATUS
"Neither rpmbuild nor dpkg-buildpackage found; 'package' target not available"
)
return()
endif()
set(package_env
SRC_DIR=${CMAKE_SOURCE_DIR}
BUILD_DIR=${CMAKE_BINARY_DIR}
PKG_VERSION=${xrpld_version}
PKG_RELEASE=${pkg_release}
)
add_custom_target(
package
COMMAND
${CMAKE_COMMAND} -E env ${package_env}
${CMAKE_SOURCE_DIR}/package/build_pkg.sh
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS xrpld
COMMENT "Building Linux package (deb/rpm inferred from host tooling)"
VERBATIM
)

View File

@@ -3,6 +3,8 @@
"requires": [
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
"wasmi/1.0.9#1fecdab9b90c96698eb35ea99ca4f5cb%1772227278.324",
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1776096494.149",
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",

View File

@@ -34,6 +34,7 @@ class Xrpl(ConanFile):
"openssl/3.6.2",
"secp256k1/0.7.1",
"soci/4.0.3",
"wasmi/1.0.9",
"zlib/1.3.2",
]
@@ -214,6 +215,7 @@ class Xrpl(ConanFile):
"soci::soci",
"secp256k1::secp256k1",
"sqlite3::sqlite",
"wasmi::wasmi",
"xxhash::xxhash",
"zlib::zlib",
]

View File

@@ -7,6 +7,8 @@ ignorePaths:
- cmake/**
- LICENSE.md
- .clang-tidy
- src/test/app/wasm_fixtures/**/*.wat
- src/test/app/wasm_fixtures/*.c
language: en
allowCompoundWords: true # TODO (#6334)
ignoreRandomStrings: true
@@ -65,6 +67,7 @@ words:
- Btrfs
- Buildx
- canonicality
- cdylib
- changespq
- checkme
- choco
@@ -99,12 +102,15 @@ words:
- desync
- desynced
- determ
- disablerepo
- distro
- doxyfile
- dxrpl
- enabled
- enablerepo
- endmacro
- exceptioned
- EXPECT_STREQ
- Falco
- fcontext
- finalizers
@@ -162,6 +168,7 @@ words:
- Merkle
- Metafuncton
- misprediction
- missingok
- mptbalance
- MPTDEX
- mptflags
@@ -193,7 +200,9 @@ words:
- NOLINT
- NOLINTNEXTLINE
- nonxrp
- noreplace
- noripple
- notifempty
- nudb
- nullptr
- nunl
@@ -213,6 +222,7 @@ words:
- preauthorize
- preauthorizes
- preclaim
- preun
- protobuf
- protos
- ptrs
@@ -247,17 +257,20 @@ words:
- sfields
- shamap
- shamapitem
- shlibs
- sidechain
- SIGGOOD
- sle
- sles
- soci
- socidb
- SRPMS
- sslws
- statsd
- STATSDCOLLECTOR
- stissue
- stnum
- stnumber
- stobj
- stobject
- stpath
@@ -280,8 +293,8 @@ words:
- txn
- txns
- txs
- UBSAN
- ubsan
- UBSAN
- umant
- unacquired
- unambiguity
@@ -318,7 +331,6 @@ words:
- xbridge
- xchain
- ximinez
- EXPECT_STREQ
- XMACRO
- xrpkuwait
- xrpl

View File

@@ -251,6 +251,9 @@ constexpr std::uint8_t kVaultMaximumIouScale = 18;
* another vault; counted from 0 */
constexpr std::uint8_t kMaxAssetCheckDepth = 5;
/** Maximum length of a Data field in Escrow object that can be updated by WASM code. */
constexpr std::size_t kMaxWasmDataLength = 1 * 1024; // 1KB
/** A ledger index. */
using LedgerIndex = std::uint32_t;

View File

@@ -127,7 +127,9 @@ enum TEMcodes : TERUnderlyingType {
temARRAY_TOO_LARGE,
temBAD_TRANSFER_FEE,
temINVALID_INNER_BATCH,
temBAD_MPT,
temBAD_WASM,
};
//------------------------------------------------------------------------------

View File

@@ -84,7 +84,7 @@ LEDGER_ENTRY(ltNEGATIVE_UNL, 0x004e, NegativeUNL, nunl, ({
/** A ledger object which contains a list of NFTs
\sa keylet::nftpage_min, keylet::nftpage_max, keylet::nftpage
\sa keylet::nftpageMin, keylet::nftpageMax, keylet::nftpage
*/
LEDGER_ENTRY(ltNFTOKEN_PAGE, 0x0050, NFTokenPage, nft_page, ({
{sfPreviousPageMin, SoeOptional},
@@ -112,7 +112,7 @@ LEDGER_ENTRY(ltSIGNER_LIST, 0x0053, SignerList, signer_list, ({
/** A ledger object which describes a ticket.
\sa keylet::ticket
\sa keylet::kTicket
*/
LEDGER_ENTRY(ltTICKET, 0x0054, Ticket, ticket, ({
{sfAccount, SoeRequired},

View File

@@ -115,7 +115,7 @@ protected:
beast::WrappedSink sink_;
beast::Journal const j_;
AccountID const account_;
AccountID const accountID_;
XRPAmount preFeeBalance_{}; // Balance before fees.
public:

View File

@@ -21,7 +21,7 @@ class TOffer
private:
SLE::pointer entry_;
Quality quality_{};
AccountID account_;
AccountID accountID_;
Asset assetIn_;
Asset assetOut_;
@@ -53,7 +53,7 @@ public:
[[nodiscard]] AccountID const&
owner() const
{
return account_;
return accountID_;
}
/** Returns the in and out amounts.
@@ -122,7 +122,7 @@ public:
isFunded() const
{
// Offer owner is issuer; they have unlimited funds if IOU
return account_ == assetOut_.getIssuer() && assetOut_.holds<Issue>();
return accountID_ == assetOut_.getIssuer() && assetOut_.holds<Issue>();
}
static std::pair<std::uint32_t, std::uint32_t>
@@ -159,7 +159,7 @@ public:
template <StepAmount TIn, StepAmount TOut>
TOffer<TIn, TOut>::TOffer(SLE::pointer entry, Quality quality)
: entry_(std::move(entry)), quality_(quality), account_(entry_->getAccountID(sfAccount))
: entry_(std::move(entry)), quality_(quality), accountID_(entry_->getAccountID(sfAccount))
{
auto const tp = entry_->getFieldAmount(sfTakerPays);
auto const tg = entry_->getFieldAmount(sfTakerGets);

View File

@@ -22,7 +22,7 @@ public:
private:
// Tx account owner is required to get the AMM trading fee in BookStep
AccountID account_;
AccountID accountID_;
// true if payment has multiple paths
bool multiPath_{false};
// Is true if AMM offer is consumed during a payment engine iteration.
@@ -31,7 +31,8 @@ private:
std::uint16_t ammIters_{0};
public:
AMMContext(AccountID const& account, bool multiPath) : account_(account), multiPath_(multiPath)
AMMContext(AccountID const& account, bool multiPath)
: accountID_(account), multiPath_(multiPath)
{
}
~AMMContext() = default;
@@ -80,7 +81,7 @@ public:
[[nodiscard]] AccountID
account() const
{
return account_;
return accountID_;
}
/** Strand execution may fail. Reset the flag at the start

View File

@@ -0,0 +1,516 @@
#pragma once
#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>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <stdexcept>
#include <string>
namespace xrpl {
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>
floatFromSTAmountImpl(STAmount const& x, int32_t mode);
Expected<Bytes, HostFunctionError>
floatFromSTNumberImpl(STNumber const& x, int32_t mode);
Expected<int64_t, HostFunctionError>
floatToIntImpl(Slice const& x, int32_t mode);
Expected<FloatPair, HostFunctionError>
floatToMantExpImpl(Slice const& x);
Expected<Bytes, HostFunctionError>
floatFromMantExpImpl(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);
} // namespace wasm_float
// Intended to work only through wasm runtime. Don't call them directly, except with unit tests
class HostFunctions
{
protected:
RTOptRef rt_;
beast::Journal j_;
public:
HostFunctions(beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) : j_(j)
{
}
void
setRT(WasmRuntimeWrapper& rt)
{
rt_ = rt;
}
void
resetRT()
{
rt_ = std::nullopt;
}
[[nodiscard]] WasmRuntimeWrapper&
getRT() const
{
if (!rt_)
Throw<std::logic_error>("Wasm runtime not set");
return rt_->get();
}
[[nodiscard]] beast::Journal
getJournal() const
{
return j_;
}
// LCOV_EXCL_START
[[nodiscard]] virtual bool
checkSelf() const
{
return true;
}
virtual Expected<std::uint32_t, HostFunctionError>
getLedgerSqn() const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<std::uint32_t, HostFunctionError>
getParentLedgerTime() const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Hash, HostFunctionError>
getParentLedgerHash() const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<uint32_t, HostFunctionError>
getBaseFee() const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
isAmendmentEnabled(uint256 const& amendmentId) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
isAmendmentEnabled(std::string_view const& amendmentName) const
{
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) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
getCurrentLedgerObjField(SField const& fname) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
getLedgerObjField(int32_t cacheIdx, SField const& fname) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
getTxNestedField(FieldLocator const& locator) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
getCurrentLedgerObjNestedField(FieldLocator const& locator) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
getLedgerObjNestedField(int32_t cacheIdx, FieldLocator const& locator) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
getTxArrayLen(SField const& fname) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
getCurrentLedgerObjArrayLen(SField const& fname) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
getTxNestedArrayLen(FieldLocator const& locator) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
getCurrentLedgerObjNestedArrayLen(FieldLocator const& locator) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
getLedgerObjNestedArrayLen(int32_t cacheIdx, FieldLocator const& locator) const
{
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) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Hash, HostFunctionError>
computeSha512HalfHash(Slice const& data) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
accountKeylet(AccountID const& account) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
ammKeylet(Asset const& issue1, Asset const& issue2) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
checkKeylet(AccountID const& account, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType)
const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
didKeylet(AccountID const& account) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
delegateKeylet(AccountID const& account, AccountID const& authorize) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
depositPreauthKeylet(AccountID const& account, AccountID const& authorize) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
escrowKeylet(AccountID const& account, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
mptokenKeylet(MPTID const& mptid, AccountID const& holder) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
nftOfferKeylet(AccountID const& account, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
offerKeylet(AccountID const& account, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
oracleKeylet(AccountID const& account, std::uint32_t docId) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
signersKeylet(AccountID const& account) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
ticketKeylet(AccountID const& account, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
vaultKeylet(AccountID const& account, std::uint32_t seq) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
getNFT(AccountID const& account, uint256 const& nftId) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
getNFTIssuer(uint256 const& nftId) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<std::uint32_t, HostFunctionError>
getNFTTaxon(uint256 const& nftId) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
getNFTFlags(uint256 const& nftId) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
getNFTTransferFee(uint256 const& nftId) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<std::uint32_t, HostFunctionError>
getNFTSerial(uint256 const& nftId) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
trace(std::string_view const& msg, Slice const& data, bool asHex) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
traceNum(std::string_view const& msg, int64_t data) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
traceAccount(std::string_view const& msg, AccountID const& account) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
traceFloat(std::string_view const& msg, Slice const& data) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
traceAmount(std::string_view const& msg, STAmount const& amount) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatFromInt(int64_t x, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatFromUint(uint64_t x, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatFromSTAmount(STAmount const& x, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatFromSTNumber(STNumber const& x, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int64_t, HostFunctionError>
floatToInt(Slice const& x, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<FloatPair, HostFunctionError>
floatToMantExp(Slice const& x) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<int32_t, HostFunctionError>
floatCompare(Slice const& x, Slice const& y) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatAdd(Slice const& x, Slice const& y, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatSubtract(Slice const& x, Slice const& y, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatMultiply(Slice const& x, Slice const& y, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatDivide(Slice const& x, Slice const& y, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatRoot(Slice const& x, int32_t n, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual Expected<Bytes, HostFunctionError>
floatPower(Slice const& x, int32_t n, int32_t mode) const
{
return Unexpected(HostFunctionError::Internal);
}
virtual ~HostFunctions() = default;
// LCOV_EXCL_STOP
};
using HFRef = std::reference_wrapper<HostFunctions>;
} // namespace xrpl

View File

@@ -0,0 +1,283 @@
#pragma once
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/wasm/HostFunc.h>
namespace xrpl {
// Intended to work only through wasm runtime. Don't call them directly, except with unit tests
class WasmHostFunctionsImpl : public HostFunctions
{
ApplyContext& ctx_;
Keylet leKey_;
mutable std::optional<std::shared_ptr<SLE const>> currentLedgerObj_;
static int constexpr maxCache = 256;
std::array<std::shared_ptr<SLE const>, maxCache> cache_;
std::optional<Bytes> data_;
public:
Expected<std::shared_ptr<SLE const>, HostFunctionError>
getCurrentLedgerObj() const
{
if (!currentLedgerObj_)
currentLedgerObj_ = ctx_.view().read(leKey_);
if (*currentLedgerObj_)
return *currentLedgerObj_;
return Unexpected(HostFunctionError::LedgerObjNotFound);
}
Expected<int32_t, HostFunctionError>
normalizeCacheIndex(int32_t cacheIdx) const
{
--cacheIdx;
if (cacheIdx < 0 || cacheIdx >= maxCache)
return Unexpected(HostFunctionError::SlotOutRange);
if (!cache_[cacheIdx])
return Unexpected(HostFunctionError::EmptySlot);
return cacheIdx;
}
template <typename F>
void
log(std::string_view const& msg, F&& dataFn) const
{
#ifdef DEBUG_OUTPUT
auto& j = std::cerr;
#else
if (!getJournal().active(beast::Severity::Trace))
return;
auto j = getJournal().trace();
#endif
j << "WasmTrace[" << toShortString(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)
{
}
bool
checkSelf() const override
{
return !currentLedgerObj_ && !data_ &&
std::ranges::none_of(cache_, [](auto const& p) { return !!p; });
}
std::optional<Bytes> const&
getData() const
{
return data_;
}
Expected<std::uint32_t, HostFunctionError>
getLedgerSqn() const override;
Expected<std::uint32_t, HostFunctionError>
getParentLedgerTime() const override;
Expected<Hash, HostFunctionError>
getParentLedgerHash() const override;
Expected<std::uint32_t, HostFunctionError>
getBaseFee() const override;
Expected<int32_t, HostFunctionError>
isAmendmentEnabled(uint256 const& amendmentId) const override;
Expected<int32_t, HostFunctionError>
isAmendmentEnabled(std::string_view const& amendmentName) const override;
Expected<int32_t, HostFunctionError>
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override;
Expected<Bytes, HostFunctionError>
getTxField(SField const& fname) const override;
Expected<Bytes, HostFunctionError>
getCurrentLedgerObjField(SField const& fname) const override;
Expected<Bytes, HostFunctionError>
getLedgerObjField(int32_t cacheIdx, SField const& fname) const override;
Expected<Bytes, HostFunctionError>
getTxNestedField(FieldLocator const& locator) const override;
Expected<Bytes, HostFunctionError>
getCurrentLedgerObjNestedField(FieldLocator const& locator) const override;
Expected<Bytes, HostFunctionError>
getLedgerObjNestedField(int32_t cacheIdx, FieldLocator const& locator) const override;
Expected<int32_t, HostFunctionError>
getTxArrayLen(SField const& fname) const override;
Expected<int32_t, HostFunctionError>
getCurrentLedgerObjArrayLen(SField const& fname) const override;
Expected<int32_t, HostFunctionError>
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const override;
Expected<int32_t, HostFunctionError>
getTxNestedArrayLen(FieldLocator const& locator) const override;
Expected<int32_t, HostFunctionError>
getCurrentLedgerObjNestedArrayLen(FieldLocator const& locator) const override;
Expected<int32_t, HostFunctionError>
getLedgerObjNestedArrayLen(int32_t cacheIdx, FieldLocator const& locator) const override;
Expected<int32_t, HostFunctionError>
updateData(Slice const& data) override;
Expected<int32_t, HostFunctionError>
checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey)
const override;
Expected<Hash, HostFunctionError>
computeSha512HalfHash(Slice const& data) const override;
Expected<Bytes, HostFunctionError>
accountKeylet(AccountID const& account) const override;
Expected<Bytes, HostFunctionError>
ammKeylet(Asset const& issue1, Asset const& issue2) const override;
Expected<Bytes, HostFunctionError>
checkKeylet(AccountID const& account, std::uint32_t seq) const override;
Expected<Bytes, HostFunctionError>
credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType)
const override;
Expected<Bytes, HostFunctionError>
didKeylet(AccountID const& account) const override;
Expected<Bytes, HostFunctionError>
delegateKeylet(AccountID const& account, AccountID const& authorize) const override;
Expected<Bytes, HostFunctionError>
depositPreauthKeylet(AccountID const& account, AccountID const& authorize) const override;
Expected<Bytes, HostFunctionError>
escrowKeylet(AccountID const& account, std::uint32_t seq) const override;
Expected<Bytes, HostFunctionError>
lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency)
const override;
Expected<Bytes, HostFunctionError>
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) const override;
Expected<Bytes, HostFunctionError>
mptokenKeylet(MPTID const& mptid, AccountID const& holder) const override;
Expected<Bytes, HostFunctionError>
nftOfferKeylet(AccountID const& account, std::uint32_t seq) const override;
Expected<Bytes, HostFunctionError>
offerKeylet(AccountID const& account, std::uint32_t seq) const override;
Expected<Bytes, HostFunctionError>
oracleKeylet(AccountID const& account, std::uint32_t docId) const override;
Expected<Bytes, HostFunctionError>
paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq)
const override;
Expected<Bytes, HostFunctionError>
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) const override;
Expected<Bytes, HostFunctionError>
signersKeylet(AccountID const& account) const override;
Expected<Bytes, HostFunctionError>
ticketKeylet(AccountID const& account, std::uint32_t seq) const override;
Expected<Bytes, HostFunctionError>
vaultKeylet(AccountID const& account, std::uint32_t seq) const override;
Expected<Bytes, HostFunctionError>
getNFT(AccountID const& account, uint256 const& nftId) const override;
Expected<Bytes, HostFunctionError>
getNFTIssuer(uint256 const& nftId) const override;
Expected<std::uint32_t, HostFunctionError>
getNFTTaxon(uint256 const& nftId) const override;
Expected<int32_t, HostFunctionError>
getNFTFlags(uint256 const& nftId) const override;
Expected<int32_t, HostFunctionError>
getNFTTransferFee(uint256 const& nftId) const override;
Expected<std::uint32_t, HostFunctionError>
getNFTSerial(uint256 const& nftId) const override;
Expected<int32_t, HostFunctionError>
trace(std::string_view const& msg, Slice const& data, bool asHex) const override;
Expected<int32_t, HostFunctionError>
traceNum(std::string_view const& msg, int64_t data) const override;
Expected<int32_t, HostFunctionError>
traceAccount(std::string_view const& msg, AccountID const& account) const override;
Expected<int32_t, HostFunctionError>
traceFloat(std::string_view const& msg, Slice const& data) const override;
Expected<int32_t, HostFunctionError>
traceAmount(std::string_view const& msg, STAmount const& amount) const override;
Expected<Bytes, HostFunctionError>
floatFromInt(int64_t x, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatFromUint(uint64_t x, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatFromSTAmount(STAmount const& x, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatFromSTNumber(STNumber const& x, int32_t mode) const override;
Expected<int64_t, HostFunctionError>
floatToInt(Slice const& x, int32_t mode) const override;
Expected<FloatPair, HostFunctionError>
floatToMantExp(Slice const& x) const override;
Expected<Bytes, HostFunctionError>
floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const override;
Expected<int32_t, HostFunctionError>
floatCompare(Slice const& x, Slice const& y) const override;
Expected<Bytes, HostFunctionError>
floatAdd(Slice const& x, Slice const& y, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatSubtract(Slice const& x, Slice const& y, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatMultiply(Slice const& x, Slice const& y, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatDivide(Slice const& x, Slice const& y, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatRoot(Slice const& x, int32_t n, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatPower(Slice const& x, int32_t n, int32_t mode) const override;
};
} // namespace xrpl

View File

@@ -0,0 +1,254 @@
#pragma once
#include <xrpl/tx/wasm/WasmImportsHelper.h>
#include <wasm.h>
#include <cstdint>
namespace xrpl {
#define WASM_CB_PARAMS_LIST void *env, wasm_val_vec_t const *params, wasm_val_vec_t *results
#define WASM_SECONDARY_CB_PARAMS_LIST \
HostFunctions &hf, wasm_val_vec_t const *params, wasm_val_vec_t *results
wasm_trap_t* HostFuncMain_wrap(WASM_CB_PARAMS_LIST);
using getLedgerSqn_proto = int32_t(uint8_t*, int32_t);
wasm_trap_t* getLedgerSqn_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getParentLedgerTime_proto = int32_t(uint8_t*, int32_t);
wasm_trap_t* getParentLedgerTime_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getParentLedgerHash_proto = int32_t(uint8_t*, int32_t);
wasm_trap_t* getParentLedgerHash_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getBaseFee_proto = int32_t(uint8_t*, int32_t);
wasm_trap_t* getBaseFee_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using isAmendmentEnabled_proto = int32_t(uint8_t const*, int32_t);
wasm_trap_t* isAmendmentEnabled_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using cacheLedgerObj_proto = int32_t(uint8_t const*, int32_t, int32_t);
wasm_trap_t* cacheLedgerObj_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getTxField_proto = int32_t(int32_t, uint8_t*, int32_t);
wasm_trap_t* getTxField_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getCurrentLedgerObjField_proto = int32_t(int32_t, uint8_t*, int32_t);
wasm_trap_t* getCurrentLedgerObjField_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getLedgerObjField_proto = int32_t(int32_t, int32_t, uint8_t*, int32_t);
wasm_trap_t* getLedgerObjField_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getTxNestedField_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* getTxNestedField_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getCurrentLedgerObjNestedField_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* getCurrentLedgerObjNestedField_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getLedgerObjNestedField_proto = int32_t(int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* getLedgerObjNestedField_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getTxArrayLen_proto = int32_t(int32_t);
wasm_trap_t* getTxArrayLen_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getCurrentLedgerObjArrayLen_proto = int32_t(int32_t);
wasm_trap_t* getCurrentLedgerObjArrayLen_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getLedgerObjArrayLen_proto = int32_t(int32_t, int32_t);
wasm_trap_t* getLedgerObjArrayLen_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getTxNestedArrayLen_proto = int32_t(uint8_t const*, int32_t);
wasm_trap_t* getTxNestedArrayLen_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getCurrentLedgerObjNestedArrayLen_proto = int32_t(uint8_t const*, int32_t);
wasm_trap_t* getCurrentLedgerObjNestedArrayLen_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getLedgerObjNestedArrayLen_proto = int32_t(int32_t, uint8_t const*, int32_t);
wasm_trap_t* getLedgerObjNestedArrayLen_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using updateData_proto = int32_t(uint8_t const*, int32_t);
wasm_trap_t* updateData_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
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(WASM_SECONDARY_CB_PARAMS_LIST);
using computeSha512HalfHash_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* computeSha512HalfHash_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using accountKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* accountKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using ammKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* ammKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using checkKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* checkKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
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(WASM_SECONDARY_CB_PARAMS_LIST);
using delegateKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* delegateKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using depositPreauthKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* depositPreauthKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using didKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* didKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using escrowKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* escrowKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
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(WASM_SECONDARY_CB_PARAMS_LIST);
using mptIssuanceKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* mptIssuanceKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using mptokenKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* mptokenKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using nftOfferKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* nftOfferKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using offerKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* offerKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using oracleKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* oracleKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
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(WASM_SECONDARY_CB_PARAMS_LIST);
using permissionedDomainKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* permissionedDomainKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using signersKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* signersKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using ticketKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* ticketKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using vaultKeylet_proto =
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* vaultKeylet_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getNFT_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* getNFT_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getNFTIssuer_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* getNFTIssuer_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getNFTTaxon_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* getNFTTaxon_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getNFTFlags_proto = int32_t(uint8_t const*, int32_t);
wasm_trap_t* getNFTFlags_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getNFTTransferFee_proto = int32_t(uint8_t const*, int32_t);
wasm_trap_t* getNFTTransferFee_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using getNFTSerial_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
wasm_trap_t* getNFTSerial_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using trace_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, int32_t);
wasm_trap_t* trace_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using traceNum_proto = int32_t(uint8_t const*, int32_t, int64_t);
wasm_trap_t* traceNum_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using traceAccount_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
wasm_trap_t* traceAccount_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using traceFloat_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
wasm_trap_t* traceFloat_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using traceAmount_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
wasm_trap_t* traceAmount_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatFromInt_proto = int32_t(int64_t, uint8_t*, int32_t, int32_t);
wasm_trap_t* floatFromInt_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatFromUint_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
wasm_trap_t* floatFromUint_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatFromSTAmount_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
wasm_trap_t* floatFromSTAmount_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatFromSTNumber_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
wasm_trap_t* floatFromSTNumber_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatToInt_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
wasm_trap_t* floatToInt_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatToMantExp_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, uint8_t*, int32_t);
wasm_trap_t* floatToMantExp_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatFromMantExp_proto = int32_t(int64_t, int32_t, uint8_t*, int32_t, int32_t);
wasm_trap_t* floatFromMantExp_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatCompare_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
wasm_trap_t* floatCompare_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
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(WASM_SECONDARY_CB_PARAMS_LIST);
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(WASM_SECONDARY_CB_PARAMS_LIST);
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(WASM_SECONDARY_CB_PARAMS_LIST);
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(WASM_SECONDARY_CB_PARAMS_LIST);
using floatRoot_proto = int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t, int32_t);
wasm_trap_t* floatRoot_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
using floatPower_proto = int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t, int32_t);
wasm_trap_t* floatPower_wrap(WASM_SECONDARY_CB_PARAMS_LIST);
} // namespace xrpl

View File

@@ -0,0 +1,189 @@
# WASM Module for Programmable Escrows
This module provides WebAssembly (WASM) execution capabilities for programmable
escrows on the XRP Ledger. When an escrow is finished, the WASM code runs to
determine whether the escrow conditions are met, enabling custom programmable
logic for escrow release conditions.
For the full specification, see
[XLS-0102: WASM VM](https://xls.xrpl.org/xls/XLS-0102-wasm-vm.html).
## Architecture
The module follows a layered architecture:
```
┌─────────────────────────────────────────────────────────────┐
│ WasmEngine (WasmVM.h) │
│ runEscrowWasm(), preflightEscrowWasm() │
│ Host function registration │
├─────────────────────────────────────────────────────────────┤
│ WasmiEngine (WasmiVM.h) │
│ Low-level wasmi interpreter integration │
├─────────────────────────────────────────────────────────────┤
│ HostFuncWrapper │ HostFuncImpl │
│ C-style WASM bridges │ C++ implementations │
├─────────────────────────────────────────────────────────────┤
│ HostFunc (Interface) │
│ Abstract base class for host functions │
└─────────────────────────────────────────────────────────────┘
```
### Key Components
- **`WasmVM.h` / `detail/WasmVM.cpp`** - High-level facade providing:
- `WasmEngine` singleton that wraps the underlying WASM interpreter
- `runEscrowWasm()` - Execute WASM code for escrow finish
- `preflightEscrowWasm()` - Validate WASM code during preflight
- `createWasmImport()` - Register all host functions
- **`WasmiVM.h` / `detail/WasmiVM.cpp`** - Low-level integration with the
[wasmi](https://github.com/wasmi-labs/wasmi) WebAssembly interpreter:
- `WasmiEngine` - Manages WASM modules, instances, and execution
- Memory management and gas metering
- Function invocation and result handling
- **`HostFunc.h`** - Abstract `HostFunctions` base class defining the interface
for all callable host functions. Each method returns
`Expected<T, HostFunctionError>`.
- **`HostFuncImpl.h` / `detail/HostFuncImpl*.cpp`** - Concrete
`WasmHostFunctionsImpl` class that implements host functions with access to
`ApplyContext` for ledger state queries. Implementation split across files:
- `HostFuncImpl.cpp` - Core utilities (updateData, checkSignature, etc.)
- `HostFuncImplFloat.cpp` - Float/number arithmetic operations
- `HostFuncImplGetter.cpp` - Field access (transaction, ledger objects)
- `HostFuncImplKeylet.cpp` - Keylet construction functions
- `HostFuncImplLedgerHeader.cpp` - Ledger header info access
- `HostFuncImplNFT.cpp` - NFT-related queries
- `HostFuncImplTrace.cpp` - Debugging/tracing functions
- **`HostFuncWrapper.h` / `detail/HostFuncWrapper.cpp`** - C-style wrapper
functions that bridge WASM calls to C++ `HostFunctions` methods. Each host
function has:
- A `_proto` type alias defining the function signature
- A `_wrap` function that extracts parameters and calls the implementation
- **`ParamsHelper.h`** - Utilities for WASM parameter handling:
- `WASM_IMPORT_FUNC` / `WASM_IMPORT_FUNC2` macros for registration
- `wasmParams()` helper for building parameter vectors
- Type conversion between WASM and C++ types
## Host Functions
Host functions allow WASM code to interact with the XRP Ledger. They are
organized into categories:
- **Ledger Information** - Access ledger sequence, timestamps, hashes, fees
- **Transaction & Ledger Object Access** - Read fields from the transaction
and ledger objects (including the current escrow object)
- **Keylet Construction** - Build keylets to look up various ledger object types
- **Cryptography** - Signature verification and hashing
- **Float Arithmetic** - Mathematical operations for amount calculations
- **NFT Operations** - Query NFT properties
- **Tracing/Debugging** - Log messages for debugging
For the complete list of available host functions, their WASM names, and gas
costs, see the [XLS-0102 specification](https://xls.xrpl.org/xls/XLS-0102-wasm-vm.html)
or `detail/WasmVM.cpp` where they are registered via `WASM_IMPORT_FUNC2` macros.
For method signatures, see `HostFunc.h`.
## Gas Model
Each host function has an associated gas cost. The gas cost is specified when
registering the function in `detail/WasmVM.cpp`:
```cpp
WASM_IMPORT_FUNC2(i, getLedgerSqn, "get_ledger_sqn", hfs, 60);
// ^^ gas cost
```
WASM execution is metered, and if the gas limit is exceeded, execution fails.
## Entry Point
The WASM module must export a function with the name defined by
`ESCROW_FUNCTION_NAME` (currently `"finish"`). This function:
- Takes no parameters (or parameters passed via host function calls)
- Returns an `int32_t`:
- `1` (or positive): Escrow conditions are met, allow finish
- `0` (or negative): Escrow conditions are not met, reject finish
## Adding a New Host Function
To add a new host function, follow these steps:
### 1. Add to HostFunc.h (Base Class)
Add a virtual method declaration with a default implementation that returns an
error:
```cpp
virtual Expected<ReturnType, HostFunctionError>
myNewFunction(ParamType1 param1, ParamType2 param2)
{
return Unexpected(HostFunctionError::INTERNAL);
}
```
### 2. Add to HostFuncImpl.h (Declaration)
Add the method override declaration in `WasmHostFunctionsImpl`:
```cpp
Expected<ReturnType, HostFunctionError>
myNewFunction(ParamType1 param1, ParamType2 param2) override;
```
### 3. Implement in detail/HostFuncImpl\*.cpp
Add the implementation in the appropriate file:
```cpp
Expected<ReturnType, HostFunctionError>
WasmHostFunctionsImpl::myNewFunction(ParamType1 param1, ParamType2 param2)
{
// Implementation using ctx (ApplyContext) for ledger access
return result;
}
```
### 4. Add Wrapper to HostFuncWrapper.h
Add the prototype and wrapper declaration:
```cpp
using myNewFunction_proto = int32_t(uint8_t const*, int32_t, ...);
wasm_trap_t*
myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
```
### 5. Implement Wrapper in detail/HostFuncWrapper.cpp
Implement the C-style wrapper that bridges WASM to C++:
```cpp
wasm_trap_t*
myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
{
// Extract parameters from params
// Call hfs->myNewFunction(...)
// Set results and return
}
```
### 6. Register in WasmVM.cpp
Add the function registration in `setCommonHostFunctions()` or
`createWasmImport()`:
```cpp
WASM_IMPORT_FUNC2(i, myNewFunction, "my_new_function", hfs, 100);
// ^^ WASM name ^^ gas cost
```
> [!IMPORTANT]
> New host functions MUST be amendment-gated in `WasmVM.cpp`.
> Wrap the registration in an amendment check to ensure the function is only
> available after the corresponding amendment is enabled on the network.

View File

@@ -0,0 +1,211 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <functional>
#include <optional>
#include <stdexcept>
#include <vector>
namespace xrpl {
using Bytes = std::vector<std::uint8_t>;
using Hash = xrpl::uint256;
using FloatPair = std::pair<int64_t, int32_t>;
enum class HostFunctionError : int32_t {
Internal = -1,
FieldNotFound = -2,
BufferTooSmall = -3,
NoArray = -4,
NotLeafField = -5,
LocatorMalformed = -6,
SlotOutRange = -7,
SlotsFull = -8,
EmptySlot = -9,
LedgerObjNotFound = -10,
Decoding = -11,
DataFieldTooLarge = -12,
PointerOutOfBounds = -13,
NoMemExported = -14,
InvalidParams = -15,
InvalidAccount = -16,
InvalidField = -17,
IndexOutOfBounds = -18,
FloatInputMalformed = -19,
FloatComputationError = -20,
NoRuntime = -21,
OutOfGas = -22,
OutOfTransferLimit = -23,
};
enum class WasmTypes { WtI32, WtI64 };
struct Wmem
{
std::uint8_t* p = nullptr;
std::size_t s = 0;
Wmem() = default;
Wmem(void* ptr, std::size_t size) : p(reinterpret_cast<std::uint8_t*>(ptr)), s(size)
{
}
};
template <typename T>
struct WasmResult
{
T result;
int64_t cost;
};
using EscrowResult = WasmResult<int32_t>;
class FieldLocator
{
int32_t const* ptr_ = nullptr;
uint32_t size_ = 0;
std::vector<int32_t> buf_;
public:
FieldLocator(std::vector<int32_t>&& buf)
: ptr_(&buf[0]), size_(buf.size()), buf_(std::move(buf))
{
}
FieldLocator(int32_t const* ptr, uint32_t const size) : ptr_(ptr), size_(size)
{
}
FieldLocator(FieldLocator const&) = delete;
FieldLocator&
operator=(FieldLocator const&) = delete;
FieldLocator(FieldLocator&&) = default;
FieldLocator&
operator=(FieldLocator&&) = default;
int32_t
operator[](unsigned i) const
{
if (i >= size_)
Throw<std::runtime_error>("index out of bounds");
return ptr_[i];
}
[[nodiscard]] uint32_t
size() const
{
return size_;
}
[[nodiscard]] int32_t const*
data() const
{
return ptr_;
}
[[nodiscard]] bool
empty() const
{
return size_ == 0;
}
};
class WasmRuntimeWrapper
{
public:
virtual ~WasmRuntimeWrapper() = default;
virtual Wmem
getMem() = 0;
virtual std::int64_t
getGas() = 0;
virtual std::int64_t
setGas(std::int64_t gas) = 0;
};
using RTOptRef = std::optional<std::reference_wrapper<WasmRuntimeWrapper>>;
struct WasmParam
{
// We are not supporting float/double
WasmTypes type = WasmTypes::WtI32;
union
{
std::int32_t i32;
std::int64_t i64 = 0;
} of;
};
template <class... Types>
inline void
wasmParamsHlp(std::vector<WasmParam>& v, std::int32_t p, Types&&... args)
{
v.push_back({.type = WasmTypes::WtI32, .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 = WasmTypes::WtI64, .of = {.i64 = p}});
wasmParamsHlp(v, 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)>
constexpr T
adjustWasmEndianessHlp(T x)
{
static_assert(std::is_integral_v<T>, "Only integral types");
if constexpr (Size > 1)
{
using U = std::make_unsigned_t<T>;
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)>
constexpr T
adjustWasmEndianess(T x)
{
// LCOV_EXCL_START
static_assert(std::is_integral_v<T>, "Only integral types");
if constexpr (std::endian::native == std::endian::big)
{
return adjustWasmEndianessHlp(x);
}
return x;
// LCOV_EXCL_STOP
}
constexpr int32_t
hfErrorToInt(HostFunctionError e)
{
return static_cast<int32_t>(e);
}
} // namespace xrpl

View File

@@ -0,0 +1,128 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/tx/wasm/HostFunc.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 <wasm.h>
namespace bft = boost::function_types;
namespace xrpl {
using wasmSecondaryCbFuncType =
wasm_trap_t*(HostFunctions&, wasm_val_vec_t const*, wasm_val_vec_t*);
struct WasmImportFunc
{
std::string_view name;
std::optional<WasmTypes> result;
std::vector<WasmTypes> params;
wasmSecondaryCbFuncType* wrap = nullptr;
uint32_t gas = 0;
};
using WasmUserData = std::pair<HFRef, WasmImportFunc>;
// string - import function name
using ImportVec = std::unordered_map<std::string_view, WasmUserData>;
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(WasmTypes::WtI32);
}
else if constexpr (std::is_same_v<at, std::int32_t>)
{
e.params.push_back(WasmTypes::WtI32);
}
else if constexpr (std::is_same_v<at, std::int64_t>)
{
e.params.push_back(WasmTypes::WtI64);
}
else
{
static_assert(std::is_pointer_v<at>, "Unsupported argument type");
}
return WasmImpArgs<N + 1, C, Mpl>(e);
}
return;
}
template <typename>
inline constexpr bool wasmDependentFalse = false;
template <typename Rt>
void
WasmImpRet(WasmImportFunc& e)
{
if constexpr (std::is_pointer_v<Rt>)
{
e.result = WasmTypes::WtI32;
}
else if constexpr (std::is_same_v<Rt, std::int32_t>)
{
e.result = WasmTypes::WtI32;
}
else if constexpr (std::is_same_v<Rt, std::int64_t>)
{
e.result = WasmTypes::WtI64;
}
else if constexpr (std::is_void_v<Rt>)
{
e.result.reset();
}
else
{
static_assert(wasmDependentFalse<Rt>, "Unsupported return type");
}
}
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));
}
// imp_name - string literal, must have static lifetime
template <typename F>
void
WasmImpFunc(
ImportVec& v,
std::string_view impName,
wasmSecondaryCbFuncType* fWrap,
HostFunctions& hf,
uint32_t gas = 0)
{
WasmImportFunc e;
e.name = impName;
e.wrap = fWrap;
e.gas = gas;
WasmImpFuncHelper<F>(e);
v.emplace(impName, std::make_pair(HFRef(hf), std::move(e)));
}
#define WASM_IMPORT_FUNC(v, f, ...) WasmImpFunc<f##_proto>(v, #f, &f##_wrap, ##__VA_ARGS__)
// n - string literal name, must have static lifetime
#define WASM_IMPORT_FUNC2(v, f, n, ...) WasmImpFunc<f##_proto>(v, n, &f##_wrap, ##__VA_ARGS__)
} // namespace xrpl

View File

@@ -0,0 +1,89 @@
#pragma once
#include <xrpl/tx/wasm/HostFunc.h>
#include <xrpl/tx/wasm/WasmImportsHelper.h>
#include <string_view>
namespace xrpl {
std::string_view inline constexpr wEnv = "env";
std::string_view inline constexpr wHostLib = "host_lib";
std::string_view inline constexpr wMem = "memory";
std::string_view inline constexpr wStore = "store";
std::string_view inline constexpr wLoad = "load";
std::string_view inline constexpr wSize = "size";
std::string_view inline constexpr wAlloc = "allocate";
std::string_view inline constexpr wDealloc = "deallocate";
std::string_view inline constexpr wProcExit = "proc_exit";
std::string_view inline constexpr escrowFunctionName = "finish";
uint32_t inline constexpr maxPages = 128; // 8MB = 64KB*128
class WasmiEngine;
class WasmEngine
{
std::unique_ptr<WasmiEngine> const impl_;
WasmEngine();
public:
WasmEngine(WasmEngine const&) = delete;
WasmEngine(WasmEngine&&) = delete;
WasmEngine&
operator=(WasmEngine const&) = delete;
WasmEngine&
operator=(WasmEngine&&) = delete;
static WasmEngine&
instance();
Expected<WasmResult<int32_t>, TER>
run(Bytes const& wasmCode,
HostFunctions& hfs,
int64_t gasLimit,
std::string_view funcName = {},
std::vector<WasmParam> const& params = {},
ImportVec const& imports = {},
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
NotTEC
check(
Bytes const& wasmCode,
HostFunctions& hfs,
std::string_view funcName,
std::vector<WasmParam> const& params = {},
ImportVec const& imports = {},
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
// Host functions helper functionality
void*
newTrap(std::string const& txt = std::string());
[[nodiscard]] beast::Journal
getJournal() const;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ImportVec
createWasmImport(HostFunctions& hfs);
Expected<EscrowResult, TER>
runEscrowWasm(
Bytes const& wasmCode,
HostFunctions& hfs,
int64_t gasLimit,
std::string_view funcName = escrowFunctionName,
std::vector<WasmParam> const& params = {});
NotTEC
preflightEscrowWasm(
Bytes const& wasmCode,
HostFunctions& hfs,
std::string_view funcName = escrowFunctionName,
std::vector<WasmParam> const& params = {});
} // namespace xrpl

View File

@@ -0,0 +1,434 @@
#pragma once
#include <xrpl/protocol/Protocol.h>
#include <xrpl/tx/wasm/WasmVM.h>
#include <wasm.h>
#include <wasmi.h>
namespace xrpl {
template <class T, void (*Create)(T*, size_t), void (*Destroy)(T*)>
class WasmVec
{
using TD = std::remove_pointer_t<decltype(T::data)>;
T vec_;
public:
WasmVec(size_t s = 0) : vec_ WASM_EMPTY_VEC
{
if (s > 0)
Create(&vec_, s); // zeroes memory
}
~WasmVec()
{
clear();
}
WasmVec(WasmVec const&) = delete;
WasmVec&
operator=(WasmVec const&) = delete;
WasmVec(WasmVec&& other) noexcept : vec_ WASM_EMPTY_VEC
{
*this = std::move(other);
}
WasmVec&
operator=(WasmVec&& other) noexcept
{
if (this != &other)
{
clear();
vec_ = other.vec_;
other.vec_ = WASM_EMPTY_VEC;
}
return *this;
}
void
clear()
{
Destroy(&vec_); // call destructor for every elements too
vec_ = WASM_EMPTY_VEC;
}
T
release()
{
T result = vec_;
vec_ = WASM_EMPTY_VEC;
return result;
}
T*
get()
{
return &vec_;
}
[[nodiscard]] T const*
get() const
{
return &vec_;
}
TD&
operator[](size_t i)
{
if (i >= vec_.size)
Throw<std::runtime_error>("Out of bound");
return vec_.data[i];
}
TD const&
operator[](size_t i) const
{
if (i >= vec_.size)
Throw<std::runtime_error>("Out of bound");
return vec_.data[i];
}
[[nodiscard]] size_t
size() const
{
return vec_.size;
}
[[nodiscard]] bool
empty() const
{
return vec_.size == 0u;
}
};
using WasmValtypeVec =
WasmVec<wasm_valtype_vec_t, &wasm_valtype_vec_new_uninitialized, &wasm_valtype_vec_delete>;
using WasmValVec = WasmVec<wasm_val_vec_t, &wasm_val_vec_new_uninitialized, &wasm_val_vec_delete>;
using WasmExternVec =
WasmVec<wasm_extern_vec_t, &wasm_extern_vec_new_uninitialized, &wasm_extern_vec_delete>;
using WasmExporttypeVec = WasmVec<
wasm_exporttype_vec_t,
&wasm_exporttype_vec_new_uninitialized,
&wasm_exporttype_vec_delete>;
using WasmImporttypeVec = WasmVec<
wasm_importtype_vec_t,
&wasm_importtype_vec_new_uninitialized,
&wasm_importtype_vec_delete>;
struct WasmiResult
{
WasmValVec r;
bool f{false}; // failure flag
WasmiResult(unsigned n = 0) : r(n)
{
}
WasmiResult() = delete;
~WasmiResult() = default;
WasmiResult(WasmiResult&& o) = default;
WasmiResult&
operator=(WasmiResult&& o) = default;
};
using ModulePtr = std::unique_ptr<wasm_module_t, decltype(&wasm_module_delete)>;
using InstancePtr = std::unique_ptr<wasm_instance_t, decltype(&wasm_instance_delete)>;
using EnginePtr = std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>;
using StorePtr = std::unique_ptr<wasm_store_t, decltype(&wasm_store_delete)>;
using FuncInfo = std::pair<wasm_func_t const*, wasm_functype_t const*>;
class InstanceWrapper
{
wasm_store_t* store_ = nullptr;
WasmExternVec exports_;
mutable int memIdx_ = -1;
InstancePtr instance_;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
private:
static InstancePtr
init(
StorePtr& s,
ModulePtr& m,
WasmExternVec& expt,
WasmExternVec const& imports,
beast::Journal j);
public:
InstanceWrapper() : instance_(nullptr, &wasm_instance_delete) {};
InstanceWrapper(InstanceWrapper const&) = delete;
InstanceWrapper(InstanceWrapper&& o) : instance_(nullptr, &wasm_instance_delete)
{
*this = std::move(o); // LCOV_EXCL_LINE
}
InstanceWrapper(StorePtr& s, ModulePtr& m, WasmExternVec const& imports, beast::Journal j)
: store_(s.get()), instance_(init(s, m, exports_, imports, j)), j_(j)
{
}
InstanceWrapper&
operator=(InstanceWrapper&& o);
InstanceWrapper&
operator=(InstanceWrapper const&) = delete;
operator bool() const
{
return static_cast<bool>(instance_);
}
FuncInfo
getFunc(std::string_view funcName, WasmExporttypeVec const& exportTypes) const;
Wmem
getMem() const;
std::int64_t
getGas() const;
std::int64_t
setGas(std::int64_t) const;
};
class ModuleWrapper
{
ModulePtr module_;
InstanceWrapper instanceWrap_;
WasmExporttypeVec exportTypes_;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
public:
// LCOV_EXCL_START
ModuleWrapper() : module_(nullptr, &wasm_module_delete)
{
}
ModuleWrapper(ModuleWrapper&& o) : module_(nullptr, &wasm_module_delete)
{
*this = std::move(o);
}
// LCOV_EXCL_STOP
ModuleWrapper&
operator=(ModuleWrapper&& o);
ModuleWrapper(
StorePtr& s,
Bytes const& wasmBin,
bool instantiate,
ImportVec const& imports,
beast::Journal j);
~ModuleWrapper() = default;
operator bool() const
{
return instanceWrap_;
}
FuncInfo
getFunc(std::string_view funcName) const
{
return instanceWrap_.getFunc(funcName, exportTypes_);
}
wasm_functype_t*
getFuncType(std::string_view funcName) const;
Wmem
getMem() const
{
return instanceWrap_.getMem();
}
InstanceWrapper&
getInstance(int i = 0)
{
return instanceWrap_;
}
InstanceWrapper const&
getInstance(int i = 0) const
{
return instanceWrap_;
}
int
addInstance(StorePtr& s, WasmExternVec const& imports)
{
instanceWrap_ = {s, module_, imports, j_};
return 0;
}
std::int64_t
getGas() const
{
return instanceWrap_ ? instanceWrap_.getGas() : -1;
}
private:
static ModulePtr
init(StorePtr& s, Bytes const& wasmBin, beast::Journal j);
WasmExternVec
buildImports(StorePtr& s, ImportVec const& imports) const;
};
class WasmiEngine
{
EnginePtr engine_;
StorePtr store_;
std::unique_ptr<ModuleWrapper> moduleWrap_;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
std::mutex m_; // 1 instance mutex
public:
WasmiEngine() : engine_(init()), store_(nullptr, &wasm_store_delete)
{
}
~WasmiEngine() = default;
static EnginePtr
init();
Expected<WasmResult<int32_t>, TER>
run(Bytes const& wasmCode,
HostFunctions& hfs,
int64_t gas,
std::string_view funcName,
std::vector<WasmParam> const& params,
ImportVec const& imports,
beast::Journal j);
NotTEC
check(
Bytes const& wasmCode,
HostFunctions& hfs,
std::string_view funcName,
std::vector<WasmParam> const& params,
ImportVec const& imports,
beast::Journal j);
[[nodiscard]] std::int64_t
getGas() const
{
return moduleWrap_ ? moduleWrap_->getGas() : -1; // LCOV_EXCL_LINE
}
// Host functions helper functionality
wasm_trap_t*
newTrap(std::string const& msg);
// LCOV_EXCL_START
[[nodiscard]] beast::Journal
getJournal() const
{
return j_;
}
// LCOV_EXCL_STOP
private:
[[nodiscard]] InstanceWrapper&
getRT(int m = 0, int i = 0) const
{
if (!moduleWrap_)
Throw<std::runtime_error>("no module");
return moduleWrap_->getInstance(i);
}
[[nodiscard]] Wmem
getMem() const
{
return moduleWrap_ ? moduleWrap_->getMem() : Wmem();
}
Expected<WasmResult<int32_t>, TER>
runHlp(
Bytes const& wasmCode,
HostFunctions& hfs,
int64_t gas,
std::string_view funcName,
std::vector<WasmParam> const& params,
ImportVec const& imports,
beast::Journal j);
NotTEC
checkHlp(
Bytes const& wasmCode,
HostFunctions& hfs,
std::string_view funcName,
std::vector<WasmParam> const& params,
ImportVec const& imports,
beast::Journal j);
int
addModule(Bytes const& wasmCode, bool instantiate, ImportVec const& imports, int64_t gas);
void
clearModules();
// int addInstance();
int32_t
runFunc(std::string_view const funcName, int32_t p);
int32_t
makeModule(Bytes const& wasmCode, WasmExternVec const& imports = {});
[[nodiscard]] FuncInfo
getFunc(std::string_view funcName) const
{
return moduleWrap_->getFunc(funcName);
}
static std::vector<wasm_val_t>
convertParams(std::vector<WasmParam> const& params);
static int
compareParamTypes(wasm_valtype_vec_t const* ftp, std::vector<wasm_val_t> const& p);
static void
addParam(std::vector<wasm_val_t>& in, int32_t p);
static void
addParam(std::vector<wasm_val_t>& in, int64_t p);
template <int NR, class... Types>
inline WasmiResult
call(std::string_view func, Types&&... args);
template <int NR, class... Types>
inline WasmiResult
call(FuncInfo const& f, Types&&... args);
template <int NR, class... Types>
inline WasmiResult
call(FuncInfo const& f, std::vector<wasm_val_t>& in);
template <int NR, class... Types>
inline WasmiResult
call(FuncInfo const& f, std::vector<wasm_val_t>& in, std::int32_t p, Types&&... args);
template <int NR, class... Types>
inline WasmiResult
call(FuncInfo const& f, std::vector<wasm_val_t>& in, std::int64_t p, Types&&... args);
template <int NR, class... Types>
inline WasmiResult
call(
FuncInfo const& f,
std::vector<wasm_val_t>& in,
uint8_t const* d,
int32_t sz,
Types&&... args);
template <int NR, class... Types>
inline WasmiResult
call(FuncInfo const& f, std::vector<wasm_val_t>& in, Bytes const& p, Types&&... args);
};
} // namespace xrpl

175
package/README.md Normal file
View File

@@ -0,0 +1,175 @@
# Linux Packaging
This directory contains all files needed to build RPM and Debian packages for `xrpld`.
## Directory layout
```
package/
build_pkg.sh Staging and build script (called by CMake targets and CI)
rpm/
xrpld.spec RPM spec (xrpld_version/pkg_release passed via rpmbuild --define)
debian/ Debian control files (control, rules, install, links, conffiles, ...)
shared/
xrpld.service systemd unit file (used by both RPM and DEB)
xrpld.sysusers sysusers.d config (used by both RPM and DEB)
xrpld.tmpfiles tmpfiles.d config (used by both RPM and DEB)
xrpld.logrotate logrotate config (installed to /etc/logrotate.d/xrpld)
update-xrpld auto-update script (installed to /usr/libexec/xrpld/, run by update-xrpld.timer)
```
## Prerequisites
Packaging targets and their container images are declared in
[`.github/scripts/strategy-matrix/linux.json`](../.github/scripts/strategy-matrix/linux.json)
via a `"package": true` field on specific os entries. Today only
`linux/amd64` is emitted; the architecture is hardcoded in `generate.py`
and the workflow runner. The package format
(deb or rpm) is inferred at build time from the container's package manager
(`apt-get` -> deb, `dnf`/`yum` -> rpm). The image tag is composed as
`ghcr.io/xrplf/ci/{distro}-{version}:{compiler}-{cver}-sha-{image_sha}`
the same scheme used by `reusable-build-test.yml`. Bump `image_sha` in
`linux.json` and both CI and local builds pick up the new image with no
workflow edits.
| Package type | Image (derived from `linux.json`) | Tool required |
| ------------ | ---------------------------------------------------- | --------------------------------------------------------------- |
| RPM | `ghcr.io/xrplf/ci/rhel-9:gcc-12-sha-<git_sha>` | `rpmbuild` |
| DEB | `ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12-sha-<git_sha>` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` |
To print the exact image tags for the current `linux.json`:
```bash
./.github/scripts/strategy-matrix/generate.py --packaging --config=.github/scripts/strategy-matrix/linux.json
```
## Building packages
### Via CI
Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call
`reusable-strategy-matrix.yml` with `mode: packaging` to generate the matrix of
`{artifact_name, os}` entries, then fan out to
`reusable-package.yml` per entry. That workflow downloads the pre-built `xrpld`
binary artifact, detects the package format from the container, and calls
`build_pkg.sh` directly — no CMake configure or build step is needed inside
the packaging job.
### Locally (mirrors CI)
With an `xrpld` binary already built at `build/xrpld`, run the packaging step
inside the same container CI uses. The image tag is derived from `linux.json`
so you don't need to hardcode a SHA.
```bash
# From the repo root. Pick any image flagged with `"package": true` in
# linux.json; the package format is inferred from the container's package
# manager. Example for the rpm-producing image:
IMAGE=$(jq -r '
.os | map(select(.package == true))[0] |
"ghcr.io/xrplf/ci/\(.distro_name)-\(.distro_version):\(.compiler_name)-\(.compiler_version)-sha-\(.image_sha)"
' .github/scripts/strategy-matrix/linux.json)
VERSION=2.4.0-local
PKG_RELEASE=1
docker run --rm \
-v "$(pwd):/src" \
-w /src \
"$IMAGE" \
./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE"
# Output:
# build/debbuild/*.deb (DEB + dbgsym .ddeb)
# build/rpmbuild/RPMS/x86_64/*.rpm
```
### Via CMake (host-side target)
If you run CMake configure on a host that has `rpmbuild` or `dpkg-buildpackage`
installed natively, you can use the CMake target directly — no container
needed, but the host toolchain replaces the pinned CI image:
```bash
cmake \
-Dxrpld=ON \
-Dxrpld_version=2.4.0-local \
-Dtests=OFF \
..
cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL
```
The `cmake/XrplPackaging.cmake` module defines the target only if at least one
of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then infers the
package format from the host's package manager. The packaging script installs
to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of
`CMAKE_INSTALL_PREFIX`.
## How `build_pkg.sh` works
`build_pkg.sh` accepts long-form flags, each of which can also be set via an
environment variable. Flags override env vars; env vars override the built-in
defaults. Run `./package/build_pkg.sh --help` for the same table:
| Flag | Env var | Default | Purpose |
| -------------------------- | ------------------- | ----------------------------- | ----------------------------------- |
| `--src-dir DIR` | `SRC_DIR` | `$PWD` | repo root |
| `--build-dir DIR` | `BUILD_DIR` | `$PWD/build` | directory holding pre-built `xrpld` |
| `--pkg-version STR` | `PKG_VERSION` | parsed from `xrpld --version` | version string, e.g. `3.2.0-b1` |
| `--pkg-release N` | `PKG_RELEASE` | `1` | package release number |
| `--source-date-epoch SECS` | `SOURCE_DATE_EPOCH` | latest git commit ctime | reproducibility timestamp |
The package format (`deb` or `rpm`) is inferred from the host's package
manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). Hosts without one of those
fail early.
Flags are for explicit invocation; environment variables are intended for
CMake/systemd/CI integration. The CI workflow and the CMake `package` target
both invoke `build_pkg.sh` with no flags, configuring it entirely via env
(see `cmake/XrplPackaging.cmake`).
It resolves `SRC_DIR` and `BUILD_DIR` to absolute paths, then calls
`stage_common()` to copy the binary, config files, and shared support files
into the staging area, and invokes the platform build tool.
### RPM
1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory.
2. Copies `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`.
3. Runs `rpmbuild -bb --define "xrpld_version ..." --define "pkg_release ..."`. The spec uses manual `install` commands to place files.
4. Output: `rpmbuild/RPMS/x86_64/xrpld-*.rpm`
### DEB
1. Creates a staging source tree at `debbuild/source/` inside the build directory.
2. Stages the binary, configs, `README.md`, and `LICENSE.md`.
3. Copies `package/debian/` control files into `debbuild/source/debian/`.
4. Copies shared service/sysusers/tmpfiles into `debian/` where `dh_installsystemd`, `dh_installsysusers`, and `dh_installtmpfiles` pick them up automatically.
5. Generates a minimal `debian/changelog` (pre-release versions use `~` instead of `-`).
6. Runs `dpkg-buildpackage -b --no-sign`. `debian/rules` uses manual `install` commands.
7. Output: `debbuild/*.deb` and `debbuild/*.ddeb` (dbgsym package)
## Post-build verification
```bash
# DEB
dpkg-deb -c debbuild/*.deb | grep -E 'systemd|sysusers|tmpfiles'
lintian -I debbuild/*.deb
# RPM
rpm -qlp rpmbuild/RPMS/x86_64/*.rpm
```
## Reproducibility
The following environment variables improve build reproducibility. They are not
set automatically by `build_pkg.sh`; set them manually if needed:
```bash
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
export TZ=UTC
export LC_ALL=C.UTF-8
export GZIP=-n
export DEB_BUILD_OPTIONS="noautodbgsym reproducible=+fixfilepath"
```

192
package/build_pkg.sh Executable file
View File

@@ -0,0 +1,192 @@
#!/usr/bin/env bash
set -euo pipefail
# Build an RPM or Debian package from a pre-built xrpld binary.
#
# Flags override env vars; env vars override defaults. Env vars are intended
# for CMake/systemd/CI integration; flags are for explicit invocation.
usage() {
cat <<'EOF'
Usage: build_pkg.sh [options]
Options (each can also be set via the env var shown):
--src-dir DIR repo root [SRC_DIR; default: $PWD]
--build-dir DIR directory holding xrpld [BUILD_DIR; default: $PWD/build]
--pkg-version STR version, e.g. 3.2.0-b1 [PKG_VERSION; default: parsed from xrpld --version]
--pkg-release N package release number [PKG_RELEASE; default: 1]
--source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; default: latest git commit ctime]
-h, --help show this help and exit
EOF
}
need_arg() {
if [[ $# -lt 2 || "$2" == --* ]]; then
echo "Missing value for $1" >&2
exit 2
fi
}
# Seed from env. CLI parsing below overrides these directly.
SRC_DIR="${SRC_DIR:-}"
BUILD_DIR="${BUILD_DIR:-}"
PKG_VERSION="${PKG_VERSION:-}"
PKG_RELEASE="${PKG_RELEASE:-}"
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}"
while [[ $# -gt 0 ]]; do
case "$1" in
--src-dir) need_arg "$@"; SRC_DIR="$2"; shift 2 ;;
--build-dir) need_arg "$@"; BUILD_DIR="$2"; shift 2 ;;
--pkg-version) need_arg "$@"; PKG_VERSION="$2"; shift 2 ;;
--pkg-release) need_arg "$@"; PKG_RELEASE="$2"; shift 2 ;;
--source-date-epoch) need_arg "$@"; SOURCE_DATE_EPOCH="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*)
echo "Unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
done
SRC_DIR="$(cd "${SRC_DIR:-${PWD}}" && pwd)"
BUILD_DIR="$(cd "${BUILD_DIR:-${PWD}/build}" && pwd)"
PKG_RELEASE="${PKG_RELEASE:-1}"
if [[ -z "${PKG_VERSION}" ]]; then
PKG_VERSION="$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3; exit}')"
fi
if [[ -z "${PKG_VERSION}" ]]; then
echo "PKG_VERSION is empty (not provided and could not be derived)." >&2
exit 1
fi
VERSION="${PKG_VERSION}"
if command -v apt-get >/dev/null 2>&1; then
pkg_type=deb
elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
pkg_type=rpm
else
echo "Cannot infer pkg_type: no apt-get, dnf, or yum on PATH." >&2
exit 1
fi
if [[ -z "${SOURCE_DATE_EPOCH}" ]]; then
if git -C "$SRC_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
SOURCE_DATE_EPOCH="$(git -C "$SRC_DIR" log -1 --format=%ct)"
else
SOURCE_DATE_EPOCH="$(date +%s)"
fi
fi
export SOURCE_DATE_EPOCH
CHANGELOG_DATE="$(date -u -R -d "@$SOURCE_DATE_EPOCH")"
# Split VERSION at the first '-' into base and optional pre-release suffix.
# Examples: "3.2.0" -> ("3.2.0", ""); "3.2.0-b1" -> ("3.2.0", "b1").
VER_BASE="${VERSION%%-*}"
VER_SUFFIX="${VERSION#*-}"
[[ "${VER_SUFFIX}" == "${VERSION}" ]] && VER_SUFFIX=""
# Reject multi-segment suffixes (e.g. "beta-1", "rc1-15-gabc123"). The RPM
# Release field forbids '-', and the convention here is single-token suffixes
# like b1 or rc2. Fail early with a clear message rather than letting either
# rpmbuild blow up or silently mangling dashes into dots.
if [[ "${VER_SUFFIX}" == *-* ]]; then
echo "build_pkg.sh: multi-segment pre-release in VERSION='${VERSION}' (suffix '${VER_SUFFIX}')." >&2
echo "Use single-token suffixes like 3.2.0-b1 or 3.2.0-rc2." >&2
exit 1
fi
SHARED="${SRC_DIR}/package/shared"
DEBIAN_DIR="${SRC_DIR}/package/debian"
# Stage files that both packaging systems consume using the same filenames.
stage_common() {
local dest="$1"
mkdir -p "${dest}"
cp "${BUILD_DIR}/xrpld" "${dest}/xrpld"
cp "${SRC_DIR}/cfg/xrpld-example.cfg" "${dest}/xrpld.cfg"
cp "${SRC_DIR}/cfg/validators-example.txt" "${dest}/validators.txt"
cp "${SRC_DIR}/LICENSE.md" "${dest}/LICENSE.md"
cp "${SRC_DIR}/README.md" "${dest}/README.md"
cp "${SHARED}/xrpld.service" "${dest}/xrpld.service"
cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers"
cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles"
cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate"
cp "${SHARED}/update-xrpld" "${dest}/update-xrpld"
cp "${SHARED}/update-xrpld.service" "${dest}/update-xrpld.service"
cp "${SHARED}/update-xrpld.timer" "${dest}/update-xrpld.timer"
cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset"
}
build_rpm() {
local topdir="${BUILD_DIR}/rpmbuild"
rm -rf "${topdir}"
mkdir -p "${topdir}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
cp "${SRC_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec"
stage_common "${topdir}/SOURCES"
# RPM Version can't contain '-'. A pre-release goes in Release with a
# leading "0." so 3.2.0-b1 sorts before the final 3.2.0-<pkg_release>.
local rpm_release="${PKG_RELEASE}"
[[ -n "${VER_SUFFIX}" ]] && rpm_release="0.${VER_SUFFIX}.${PKG_RELEASE}"
set -x
rpmbuild -bb \
--define "_topdir ${topdir}" \
--define "xrpld_version ${VER_BASE}" \
--define "xrpld_release ${rpm_release}" \
"${topdir}/SPECS/xrpld.spec"
}
build_deb() {
local staging="${BUILD_DIR}/debbuild/source"
rm -rf "${staging}"
mkdir -p "${staging}"
stage_common "${staging}"
cp -r "${DEBIAN_DIR}" "${staging}/debian"
# Debhelper auto-discovers these only from debian/.
cp "${staging}/xrpld.service" "${staging}/debian/xrpld.service"
cp "${staging}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers"
cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles"
cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate"
cp "${staging}/update-xrpld.service" "${staging}/debian/xrpld.update-xrpld.service"
cp "${staging}/update-xrpld.timer" "${staging}/debian/xrpld.update-xrpld.timer"
# Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0.
local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}"
# Derive release channel from the version suffix:
# (none) -> stable (tagged release)
# b0 -> develop (develop-branch build)
# b<N>, rc<N> -> unstable (pre-release)
local deb_distribution
case "${VER_SUFFIX}" in
"") deb_distribution="stable" ;;
b0) deb_distribution="develop" ;;
*) deb_distribution="unstable" ;;
esac
cat > "${staging}/debian/changelog" <<EOF
xrpld (${deb_full_version}) ${deb_distribution}; urgency=medium
* Release ${VERSION}.
-- XRPL Foundation <contact@xrplf.org> ${CHANGELOG_DATE}
EOF
chmod +x "${staging}/debian/rules"
set -x
( cd "${staging}" && dpkg-buildpackage -b --no-sign -d )
}
"build_${pkg_type}"

23
package/debian/control Normal file
View File

@@ -0,0 +1,23 @@
Source: xrpld
Section: net
Priority: optional
Maintainer: XRPL Foundation <contact@xrplf.org>
Rules-Requires-Root: no
Build-Depends:
debhelper-compat (= 13)
Standards-Version: 4.7.0
Homepage: https://github.com/XRPLF/rippled
Vcs-Git: https://github.com/XRPLF/rippled.git
Vcs-Browser: https://github.com/XRPLF/rippled
Package: xrpld
Section: net
Priority: optional
Architecture: any
Depends:
${shlibs:Depends},
${misc:Depends}
Description: XRP Ledger daemon
Reference implementation of the XRP Ledger protocol.
Participates in the peer-to-peer network, processes transactions,
and maintains a local ledger copy.

18
package/debian/copyright Normal file
View File

@@ -0,0 +1,18 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: rippled
Source: https://github.com/XRPLF/rippled
Files: *
Copyright: 2011-present, the XRP Ledger developers
License: ISC
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

27
package/debian/rules Normal file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/make -f
export DH_VERBOSE = 1
%:
dh $@
override_dh_auto_configure override_dh_auto_build override_dh_auto_test:
@:
override_dh_installsystemd:
dh_installsystemd --no-start xrpld.service
dh_installsystemd --name=update-xrpld --no-start update-xrpld.service update-xrpld.timer
execute_before_dh_installtmpfiles:
dh_installsysusers
override_dh_installsysusers:
override_dh_install:
install -D -m 0755 xrpld debian/xrpld/usr/bin/xrpld
install -D -m 0644 xrpld.cfg debian/xrpld/etc/xrpld/xrpld.cfg
install -D -m 0644 validators.txt debian/xrpld/etc/xrpld/validators.txt
install -D -m 0755 update-xrpld debian/xrpld/usr/libexec/xrpld/update-xrpld
override_dh_dwz:
@:

View File

@@ -0,0 +1 @@
3.0 (quilt)

View File

@@ -0,0 +1,2 @@
README.md
LICENSE.md

View File

@@ -0,0 +1,2 @@
# Legacy compat symlinks (remove next major release)
usr/bin/xrpld usr/local/bin/rippled

100
package/rpm/xrpld.spec Normal file
View File

@@ -0,0 +1,100 @@
Name: xrpld
Version: %{xrpld_version}
Release: %{xrpld_release}%{?dist}
Summary: XRP Ledger daemon
License: ISC
URL: https://github.com/XRPLF/rippled
ExclusiveArch: x86_64 aarch64
BuildRequires: systemd-rpm-macros
%undefine _debugsource_packages
%debug_package
%build_mtime_policy clamp_to_source_date_epoch
%{?systemd_requires}
%{?sysusers_requires_compat}
%description
xrpld is the reference implementation of the XRP Ledger protocol. It
participates in the peer-to-peer XRP Ledger network, processes
transactions, and maintains the ledger database.
%prep
:
%build
:
%install
install -Dm0755 %{_sourcedir}/xrpld %{buildroot}%{_bindir}/%{name}
install -Dm0644 %{_sourcedir}/xrpld.cfg %{buildroot}%{_sysconfdir}/%{name}/xrpld.cfg
install -Dm0644 %{_sourcedir}/validators.txt %{buildroot}%{_sysconfdir}/%{name}/validators.txt
# systemd units, sysusers, tmpfiles, preset
install -Dm0644 %{_sourcedir}/xrpld.service %{buildroot}%{_unitdir}/xrpld.service
install -Dm0644 %{_sourcedir}/update-xrpld.service %{buildroot}%{_unitdir}/update-xrpld.service
install -Dm0644 %{_sourcedir}/update-xrpld.timer %{buildroot}%{_unitdir}/update-xrpld.timer
install -Dm0644 %{_sourcedir}/xrpld.sysusers %{buildroot}%{_sysusersdir}/xrpld.conf
install -Dm0644 %{_sourcedir}/xrpld.tmpfiles %{buildroot}%{_tmpfilesdir}/xrpld.conf
install -Dm0644 %{_sourcedir}/50-xrpld.preset %{buildroot}%{_presetdir}/50-xrpld.preset
# Logrotate config
install -Dm0644 %{_sourcedir}/xrpld.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
# Update helper
install -Dm0755 %{_sourcedir}/update-xrpld %{buildroot}%{_libexecdir}/%{name}/update-xrpld
# Docs
install -Dm0644 %{_sourcedir}/LICENSE.md %{buildroot}%{_docdir}/%{name}/LICENSE.md
install -Dm0644 %{_sourcedir}/README.md %{buildroot}%{_docdir}/%{name}/README.md
# Legacy compatibility for pre-FHS package layouts.
# TODO: remove after rippled fully deprecated.
install -d %{buildroot}/usr/local/bin
ln -s %{_bindir}/%{name} %{buildroot}/usr/local/bin/rippled
%pre
%sysusers_create_package %{name} %{_sourcedir}/xrpld.sysusers
%post
systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
%systemd_post xrpld.service update-xrpld.timer
%preun
%systemd_preun xrpld.service update-xrpld.timer
%postun
%systemd_postun_with_restart xrpld.service
%files
%license %{_docdir}/%{name}/LICENSE.md
%doc %{_docdir}/%{name}/README.md
%dir %{_sysconfdir}/%{name}
%dir %{_libexecdir}/%{name}
%{_bindir}/%{name}
%config(noreplace) %{_sysconfdir}/%{name}/xrpld.cfg
%config(noreplace) %{_sysconfdir}/%{name}/validators.txt
%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
%{_libexecdir}/%{name}/update-xrpld
%{_unitdir}/xrpld.service
%{_unitdir}/update-xrpld.service
%{_unitdir}/update-xrpld.timer
%{_presetdir}/50-xrpld.preset
%{_sysusersdir}/xrpld.conf
%{_tmpfilesdir}/xrpld.conf
%ghost %dir /var/lib/%{name}
%ghost %dir /var/log/%{name}
# Legacy compatibility for pre-FHS package layouts.
# TODO: remove after rippled fully deprecated.
/usr/local/bin/rippled

View File

@@ -0,0 +1,4 @@
# /usr/lib/systemd/system-preset/50-xrpld.preset
enable xrpld.service
# Don't enable automatic updates
disable update-xrpld.timer

152
package/shared/update-xrpld Executable file
View File

@@ -0,0 +1,152 @@
#!/usr/bin/env bash
set -euo pipefail
# Optional: also write logs to a legacy file in addition to journald.
# By default, this script logs to systemd/journald, viewable via:
# journalctl -t update-xrpld
#
# Uncomment the line below if you need a flat file for compatibility with
# external tooling, manual inspection, or environments where journald logs
# are not persisted or easily accessible.
#
# Note: This duplicates all output (stdout/stderr) to both journald and the file.
# It is generally not needed on modern systems and may cause log file growth
# if left enabled long-term.
#
# Requires /var/log/xrpld/ to exist and be writable by the service (root).
#
# exec > >(tee -a /var/log/xrpld/update.log) 2>&1
PATH=/usr/sbin:/usr/bin:/sbin:/bin
PKG_NAME=${PKG_NAME:-xrpld}
log() {
# If running under systemd/journald, let it handle timestamps.
if [[ -n "${JOURNAL_STREAM:-}" ]]; then
printf '%s\n' "$*"
else
printf '%s %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*"
fi
}
require_root() {
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
log "RESULT: failed reason=not-root"
exit 1
fi
}
get_installed_version() {
if command -v dpkg-query >/dev/null 2>&1; then
dpkg-query -W -f='${Version}' "$PKG_NAME" 2>/dev/null || printf 'unknown'
elif command -v rpm >/dev/null 2>&1; then
rpm -q --qf '%{VERSION}-%{RELEASE}' "$PKG_NAME" 2>/dev/null || printf 'unknown'
else
printf 'unknown'
fi
}
trap 'log "RESULT: failed reason=script-error exit_code=$?"' ERR
apt_can_update() {
apt-get update -qq
apt-get -s --only-upgrade install "$PKG_NAME" 2>/dev/null | grep -q "^Inst ${PKG_NAME}\b"
}
apt_apply_update() {
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" \
"$PKG_NAME"
}
get_rpm_pm() {
if command -v dnf >/dev/null 2>&1; then
printf 'dnf\n'
elif command -v yum >/dev/null 2>&1; then
printf 'yum\n'
else
return 1
fi
}
rpm_refresh_metadata() {
local pm=$1
if [[ "$pm" == "dnf" ]]; then
dnf makecache --refresh -q >/dev/null
else
yum clean expire-cache -q >/dev/null
fi
}
rpm_can_update() {
local pm=$1
rpm_refresh_metadata "$pm"
local rc=0
set +e
"$pm" check-update -q "$PKG_NAME" >/dev/null 2>&1
rc=$?
set -e
if [[ $rc -eq 100 ]]; then
return 0
elif [[ $rc -eq 0 ]]; then
return 1
else
log "$pm check-update failed with exit code ${rc}."
exit 1
fi
}
rpm_apply_update() {
local pm=$1
"$pm" update -y "$PKG_NAME"
}
restart_service() {
# Preserve the operator's prior service state: if xrpld was intentionally
# stopped before the update, don't bring it back up just because the
# auto-update timer fired.
if systemctl is-active --quiet "${PKG_NAME}.service"; then
systemctl restart "${PKG_NAME}.service"
log "${PKG_NAME} service restarted successfully."
else
log "${PKG_NAME} service was not running; skipping restart to preserve prior state."
fi
}
main() {
require_root
if command -v apt-get >/dev/null 2>&1; then
log "Checking for ${PKG_NAME} updates via apt"
if apt_can_update; then
log "Update available; installing."
apt_apply_update
restart_service
log "RESULT: updated ${PKG_NAME}=$(get_installed_version)"
else
log "RESULT: no-update ${PKG_NAME}=$(get_installed_version)"
fi
return
fi
local rpm_pm=""
if rpm_pm="$(get_rpm_pm)"; then
log "Checking for ${PKG_NAME} updates via ${rpm_pm}"
if rpm_can_update "$rpm_pm"; then
log "Update available; installing"
rpm_apply_update "$rpm_pm"
restart_service
log "RESULT: updated ${PKG_NAME}=$(get_installed_version)"
else
log "RESULT: no-update ${PKG_NAME}=$(get_installed_version)"
fi
return
fi
log "RESULT: failed reason=no-package-manager"
exit 1
}
main "$@"

View File

@@ -0,0 +1,16 @@
[Unit]
Description=Check for and install xrpld package updates
Documentation=man:systemd.service(5)
Wants=network-online.target
After=network-online.target
ConditionPathExists=/usr/libexec/xrpld/update-xrpld
ConditionPathExists=/usr/bin/xrpld
[Service]
Type=oneshot
ExecStart=/usr/bin/flock -n /run/lock/xrpld-update.lock /usr/libexec/xrpld/update-xrpld
StandardOutput=journal
StandardError=journal
SyslogIdentifier=update-xrpld
TimeoutStartSec=30min
PrivateTmp=true

View File

@@ -0,0 +1,10 @@
[Unit]
Description=Daily xrpld update check
[Timer]
OnCalendar=*-*-* 00:00:00
RandomizedDelaySec=24h
Persistent=true
[Install]
WantedBy=timers.target

View File

@@ -0,0 +1,19 @@
/var/log/xrpld/*.log {
daily
minsize 200M
rotate 7
nocreate
missingok
notifempty
compress
compresscmd /usr/bin/gzip
compressext .gz
postrotate
# Only signal the daemon if it's actually running; otherwise the RPC
# call returns a transport error and logrotate marks the rotation as
# failed, generating recurring errors on stopped nodes.
if systemctl is-active --quiet xrpld; then
/usr/bin/xrpld --conf /etc/xrpld/xrpld.cfg logrotate
fi
endscript
}

View File

@@ -0,0 +1,22 @@
[Unit]
Description=XRP Ledger Daemon
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Type=simple
ExecStart=/usr/bin/xrpld --net --silent --conf /etc/xrpld/xrpld.cfg
Restart=always
RestartSec=5s
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
User=xrpld
Group=xrpld
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1 @@
u xrpld - "XRP Ledger daemon" /var/lib/xrpld /sbin/nologin

View File

@@ -0,0 +1,2 @@
d /var/lib/xrpld 0750 xrpld xrpld -
d /var/log/xrpld 0750 xrpld xrpld -

View File

@@ -183,15 +183,15 @@ trustCreate(
bool const bSrcHigh,
AccountID const& uSrcAccountID,
AccountID const& uDstAccountID,
uint256 const& uIndex, // --> ripple state entry
SLE::ref sleAccount, // --> the account being set.
bool const bAuth, // --> authorize account.
bool const bNoRipple, // --> others cannot ripple through
bool const bFreeze, // --> funds cannot leave
bool bDeepFreeze, // --> can neither receive nor send funds
STAmount const& saBalance, // --> balance of account being set.
uint256 const& uIndex, // ripple state entry
SLE::ref sleAccount, // the account being set.
bool const bAuth, // authorize account.
bool const bNoRipple, // others cannot ripple through
bool const bFreeze, // funds cannot leave
bool bDeepFreeze, // can neither receive nor send funds
STAmount const& saBalance, // balance of account being set.
// Issuer should be noAccount()
STAmount const& saLimit, // --> limit for account being set.
STAmount const& saLimit, // limit for account being set.
// Issuer should be the account being set.
std::uint32_t uQualityIn,
std::uint32_t uQualityOut,

View File

@@ -1191,7 +1191,7 @@ directSendNoLimitMultiMPT(
// Use uint64_t, not STAmount, to keep MaximumAmount comparisons in exact
// integer arithmetic. STAmount implicitly converts to Number, whose
// small-scale mantissa (~16 digits) can lose precision for values near
// maxMPTokenAmount (19 digits).
// kMaxMpTokenAmount (19 digits).
std::uint64_t totalSendAmount{0};
std::uint64_t const maximumAmount = sle->at(~sfMaximumAmount).value_or(kMaxMpTokenAmount);
std::uint64_t const outstandingAmount = sle->getFieldU64(sfOutstandingAmount);

View File

@@ -23,7 +23,7 @@ namespace {
//------------------------------------------------------------------------------
// clang-format off
// NOLINTNEXTLINE(readability-identifier-naming)
char const* const versionString = "3.2.0-b0"
char const* const versionString = "3.3.0-b0"
// clang-format on
;

View File

@@ -199,6 +199,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."),

View File

@@ -262,7 +262,7 @@ Transactor::Transactor(ApplyContext& ctx)
: ctx_(ctx)
, sink_(ctx.journal, toShortString(ctx.tx.getTransactionID()) + " ")
, j_(sink_)
, account_(ctx.tx.getAccountID(sfAccount))
, accountID_(ctx.tx.getAccountID(sfAccount))
{
}
@@ -431,7 +431,7 @@ Transactor::payFee()
// Deduct the fee, so it's not available during the transaction.
// Will only write the account back if the transaction succeeds.
sle->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance) - feePaid);
if (feePayer != account_)
if (feePayer != accountID_)
view().update(sle); // done in `apply()` for the account
// VFALCO Should we call view().rawDestroyXRP() here as well?
@@ -544,7 +544,7 @@ Transactor::consumeSeqProxy(SLE::pointer const& sleAccount)
sleAccount->setFieldU32(sfSequence, seqProx.value() + 1);
return tesSUCCESS;
}
return ticketDelete(view(), account_, getTicketIndex(account_, seqProx), j_);
return ticketDelete(view(), accountID_, getTicketIndex(accountID_, seqProx), j_);
}
// Remove a single Ticket from the ledger.
@@ -617,7 +617,7 @@ Transactor::ticketDelete(
void
Transactor::preCompute()
{
XRPL_ASSERT(account_ != beast::kZero, "xrpl::Transactor::preCompute : nonzero account");
XRPL_ASSERT(accountID_ != beast::kZero, "xrpl::Transactor::preCompute : nonzero account");
}
TER
@@ -627,12 +627,12 @@ Transactor::apply()
// If the transactor requires a valid account and the transaction doesn't
// list one, preflight will have already a flagged a failure.
auto const sle = view().peek(keylet::account(account_));
auto const sle = view().peek(keylet::account(accountID_));
// sle must exist except for transactions
// that allow zero account.
XRPL_ASSERT(
sle != nullptr || account_ == beast::kZero,
sle != nullptr || accountID_ == beast::kZero,
"xrpl::Transactor::apply : non-null SLE or zero account");
if (sle)
@@ -1238,7 +1238,7 @@ Transactor::operator()()
if (isTecClaim(result) && ((view().flags() & TapFailHard) != 0u))
{
// If the tapFAIL_HARD flag is set, a tec result
// If the TapFailHard flag is set, a tec result
// must not do anything
ctx_.discard();
applied = false;

View File

@@ -344,7 +344,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
TER
AccountDelete::doApply()
{
auto src = view().peek(keylet::account(account_));
auto src = view().peek(keylet::account(accountID_));
XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account");
auto const dstID = ctx_.tx[sfDestination];
@@ -357,12 +357,12 @@ AccountDelete::doApply()
if (ctx_.tx.isFieldPresent(sfCredentialIDs))
{
if (auto err =
verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, dstID, dst, ctx_.journal);
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dstID, dst, ctx_.journal);
!isTesSuccess(err))
return err;
}
Keylet const ownerDirKeylet{keylet::ownerDir(account_)};
Keylet const ownerDirKeylet{keylet::ownerDir(accountID_)};
auto const ter = cleanupOnAccountDelete(
view(),
ownerDirKeylet,
@@ -371,7 +371,7 @@ AccountDelete::doApply()
std::shared_ptr<SLE>& sleItem) -> std::pair<TER, SkipEntry> {
if (auto deleter = nonObligationDeleter(nodeType))
{
TER const result{deleter(ctx_.registry, view(), account_, dirEntry, sleItem, j_)};
TER const result{deleter(ctx_.registry, view(), accountID_, dirEntry, sleItem, j_)};
return {result, SkipEntry::No};
}
@@ -402,7 +402,7 @@ AccountDelete::doApply()
// delete it.
if (view().exists(ownerDirKeylet) && !view().emptyDirDelete(ownerDirKeylet))
{
JLOG(j_.error()) << "AccountDelete cannot delete root dir node of " << toBase58(account_);
JLOG(j_.error()) << "AccountDelete cannot delete root dir node of " << toBase58(accountID_);
return tecHAS_OBLIGATIONS;
}

View File

@@ -279,7 +279,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
TER
AccountSet::doApply()
{
auto const sle = view().peek(keylet::account(account_));
auto const sle = view().peek(keylet::account(accountID_));
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -298,7 +298,7 @@ AccountSet::doApply()
bool const bSetDisallowXRP{tx.isFlag(tfDisallowXRP) || (uSetFlag == asfDisallowXRP)};
bool const bClearDisallowXRP{tx.isFlag(tfAllowXRP) || (uClearFlag == asfDisallowXRP)};
bool const sigWithMaster{[&tx, &acct = account_]() {
bool const sigWithMaster{[&tx, &acct = accountID_]() {
auto const spk = tx.getSigningPubKey();
if (publicKeyType(makeSlice(spk)))
@@ -367,7 +367,7 @@ AccountSet::doApply()
return tecNEED_MASTER_KEY;
}
if ((!sle->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signers(account_))))
if ((!sle->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signers(accountID_))))
{
// Account has no regular key or multi-signer signer list.
return tecNO_ALTERNATIVE_KEY;

View File

@@ -55,7 +55,7 @@ SetRegularKey::preflight(PreflightContext const& ctx)
TER
SetRegularKey::doApply()
{
auto const sle = view().peek(keylet::account(account_));
auto const sle = view().peek(keylet::account(accountID_));
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -69,7 +69,7 @@ SetRegularKey::doApply()
else
{
// Account has disabled master key and no multi-signer signer list.
if (sle->isFlag(lsfDisableMaster) && !view().peek(keylet::signers(account_)))
if (sle->isFlag(lsfDisableMaster) && !view().peek(keylet::signers(accountID_)))
return tecNO_ALTERNATIVE_KEY;
sle->makeFieldAbsent(sfRegularKey);

View File

@@ -300,9 +300,9 @@ SignerListSet::validateQuorumAndSignerEntries(
TER
SignerListSet::replaceSignerList()
{
auto const accountKeylet = keylet::account(account_);
auto const ownerDirKeylet = keylet::ownerDir(account_);
auto const signerListKeylet = keylet::signers(account_);
auto const accountKeylet = keylet::account(accountID_);
auto const ownerDirKeylet = keylet::ownerDir(accountID_);
auto const signerListKeylet = keylet::signers(accountID_);
// This may be either a create or a replace. Preemptively remove any
// old signer list. May reduce the reserve, so this is done before
@@ -337,9 +337,9 @@ SignerListSet::replaceSignerList()
auto viewJ = ctx_.registry.get().getJournal("View");
// Add the signer list to the account's directory.
auto const page =
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(account_));
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_));
JLOG(j_.trace()) << "Create signer list for account " << toBase58(account_) << ": "
JLOG(j_.trace()) << "Create signer list for account " << toBase58(accountID_) << ": "
<< (page ? "success" : "failure");
if (!page)
@@ -356,7 +356,7 @@ SignerListSet::replaceSignerList()
TER
SignerListSet::destroySignerList()
{
auto const accountKeylet = keylet::account(account_);
auto const accountKeylet = keylet::account(accountID_);
// Destroying the signer list is only allowed if either the master key
// is enabled or there is a regular key.
SLE::pointer const ledgerEntry = view().peek(accountKeylet);
@@ -366,8 +366,8 @@ SignerListSet::destroySignerList()
if ((ledgerEntry->isFlag(lsfDisableMaster)) && (!ledgerEntry->isFieldPresent(sfRegularKey)))
return tecNO_ALTERNATIVE_KEY;
auto const ownerDirKeylet = keylet::ownerDir(account_);
auto const signerListKeylet = keylet::signers(account_);
auto const ownerDirKeylet = keylet::ownerDir(accountID_);
auto const signerListKeylet = keylet::signers(accountID_);
return removeSignersFromLedger(
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_);
}
@@ -378,7 +378,7 @@ SignerListSet::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t
// Assign the quorum, default SignerListID, and flags.
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
{
ledgerEntry->setAccountID(sfOwner, account_);
ledgerEntry->setAccountID(sfOwner, accountID_);
}
ledgerEntry->setFieldU32(sfSignerQuorum, quorum_);
ledgerEntry->setFieldU32(sfSignerListID, kDefaultSignerListId);

View File

@@ -1920,7 +1920,7 @@ XChainCommit::doApply()
// Support dipping into reserves to pay the fee
TransferHelperSubmittingAccountInfo submittingAccountInfo{
.account = account_,
.account = accountID_,
.preFeeBalance = preFeeBalance_,
.postFeeBalance = (*sleAccount)[sfBalance]};
@@ -2197,7 +2197,9 @@ XChainCreateAccountCommit::doApply()
// Support dipping into reserves to pay the fee
TransferHelperSubmittingAccountInfo submittingAccountInfo{
.account = account_, .preFeeBalance = preFeeBalance_, .postFeeBalance = (*sle)[sfBalance]};
.account = accountID_,
.preFeeBalance = preFeeBalance_,
.postFeeBalance = (*sle)[sfBalance]};
STAmount const toTransfer = amount + reward;
auto const thTer = transferHelper(
psb,

View File

@@ -294,7 +294,7 @@ CheckCash::doApply()
}
AccountID const srcId{sleCheck->getAccountID(sfAccount)};
if (!psb.exists(keylet::account(srcId)) || !psb.exists(keylet::account(account_)))
if (!psb.exists(keylet::account(srcId)) || !psb.exists(keylet::account(accountID_)))
{
// LCOV_EXCL_START
JLOG(ctx_.journal.fatal()) << "Precheck did not verify source or destination's existence.";
@@ -315,7 +315,7 @@ CheckCash::doApply()
auto viewJ = ctx_.registry.get().getJournal("View");
auto const optDeliverMin = ctx_.tx[~sfDeliverMin];
if (srcId != account_)
if (srcId != accountID_)
{
STAmount const sendMax = sleCheck->at(sfSendMax);
@@ -353,7 +353,7 @@ CheckCash::doApply()
}
// The source account has enough XRP so make the ledger change.
if (TER const ter{transferXRP(psb, srcId, account_, xrpDeliver, viewJ)};
if (TER const ter{transferXRP(psb, srcId, accountID_, xrpDeliver, viewJ)};
!isTesSuccess(ter))
{
// The transfer failed. Return the error code.
@@ -383,7 +383,7 @@ CheckCash::doApply()
// Check reserve. Return destination account SLE if enough reserve,
// otherwise return nullptr.
auto checkReserve = [&]() -> std::shared_ptr<SLE> {
auto sleDst = psb.peek(keylet::account(account_));
auto sleDst = psb.peek(keylet::account(accountID_));
// Can the account cover the trust line's or MPT reserve?
if (std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)};
@@ -405,9 +405,9 @@ CheckCash::doApply()
[&](Issue const& issue) -> std::optional<TER> {
// If a trust line does not exist yet create one.
Issue const& trustLineIssue = issue;
AccountID const truster = deliverIssuer == account_ ? srcId : account_;
AccountID const truster = deliverIssuer == accountID_ ? srcId : accountID_;
trustLineKey = keylet::line(truster, trustLineIssue);
destLow = deliverIssuer > account_;
destLow = deliverIssuer > accountID_;
if (!psb.exists(*trustLineKey))
{
@@ -433,7 +433,7 @@ CheckCash::doApply()
psb, // payment sandbox
destLow, // is dest low?
deliverIssuer, // source
account_, // destination
accountID_, // destination
trustLineKey->key, // ledger index
sleDst, // Account to add to
false, // authorize account
@@ -441,7 +441,7 @@ CheckCash::doApply()
false, // freeze trust line
false, // deep freeze trust line
initialBalance, // zero initial balance
Issue(currency, account_), // limit of zero
Issue(currency, accountID_), // limit of zero
0, // quality in
0, // quality out
viewJ); // journal
@@ -479,18 +479,18 @@ CheckCash::doApply()
return std::nullopt;
},
[&](MPTIssue const& issue) -> std::optional<TER> {
if (account_ != deliverIssuer)
if (accountID_ != deliverIssuer)
{
auto const& mptID = issue.getMptID();
// Create MPT if it doesn't exist
auto const mptokenKey = keylet::mptoken(mptID, account_);
auto const mptokenKey = keylet::mptoken(mptID, accountID_);
if (!psb.exists(mptokenKey))
{
auto sleDst = checkReserve();
if (sleDst == nullptr)
return tecINSUFFICIENT_RESERVE;
if (auto const err = checkCreateMPT(psb, mptID, account_, j_);
if (auto const err = checkCreateMPT(psb, mptID, accountID_, j_);
!isTesSuccess(err))
{
return err;
@@ -518,7 +518,7 @@ CheckCash::doApply()
psb,
flowDeliver,
srcId,
account_,
accountID_,
STPathSet{},
true, // default path
static_cast<bool>(optDeliverMin), // partial payment
@@ -556,9 +556,9 @@ CheckCash::doApply()
// Check was cashed. If not a self send (and it shouldn't be), remove
// check link from destination directory.
if (srcId != account_ &&
if (srcId != accountID_ &&
!psb.dirRemove(
keylet::ownerDir(account_), sleCheck->at(sfDestinationNode), sleCheck->key(), true))
keylet::ownerDir(accountID_), sleCheck->at(sfDestinationNode), sleCheck->key(), true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete check from destination.";

View File

@@ -175,7 +175,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
TER
CheckCreate::doApply()
{
auto const sle = view().peek(keylet::account(account_));
auto const sle = view().peek(keylet::account(accountID_));
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -192,10 +192,10 @@ CheckCreate::doApply()
// Note that we use the value from the sequence or ticket as the
// Check sequence. For more explanation see comments in SeqProxy.h.
std::uint32_t const seq = ctx_.tx.getSeqValue();
Keylet const checkKeylet = keylet::check(account_, seq);
Keylet const checkKeylet = keylet::check(accountID_, seq);
auto sleCheck = std::make_shared<SLE>(checkKeylet);
sleCheck->setAccountID(sfAccount, account_);
sleCheck->setAccountID(sfAccount, accountID_);
AccountID const dstAccountId = ctx_.tx[sfDestination];
sleCheck->setAccountID(sfDestination, dstAccountId);
sleCheck->setFieldU32(sfSequence, seq);
@@ -214,7 +214,7 @@ CheckCreate::doApply()
auto viewJ = ctx_.registry.get().getJournal("View");
// If it's not a self-send (and it shouldn't be), add Check to the
// destination's owner directory.
if (dstAccountId != account_)
if (dstAccountId != accountID_)
{
auto const page = view().dirInsert(
keylet::ownerDir(dstAccountId), checkKeylet, describeOwnerDir(dstAccountId));
@@ -229,8 +229,8 @@ CheckCreate::doApply()
}
{
auto const page =
view().dirInsert(keylet::ownerDir(account_), checkKeylet, describeOwnerDir(account_));
auto const page = view().dirInsert(
keylet::ownerDir(accountID_), checkKeylet, describeOwnerDir(accountID_));
JLOG(j_.trace()) << "Adding Check to owner directory " << to_string(checkKeylet.key) << ": "
<< (page ? "success" : "failure");

View File

@@ -89,7 +89,7 @@ CredentialAccept::doApply()
AccountID const issuer{ctx_.tx[sfIssuer]};
// Both exist as credential object exist itself (checked in preclaim)
auto const sleSubject = view().peek(keylet::account(account_));
auto const sleSubject = view().peek(keylet::account(accountID_));
auto const sleIssuer = view().peek(keylet::account(issuer));
if (!sleSubject || !sleIssuer)
@@ -103,7 +103,7 @@ CredentialAccept::doApply()
}
auto const credType(ctx_.tx[sfCredentialType]);
Keylet const credentialKey = keylet::credential(account_, issuer, credType);
Keylet const credentialKey = keylet::credential(accountID_, issuer, credType);
auto const sleCred = view().peek(credentialKey); // Checked in preclaim()
if (!sleCred)
return tefINTERNAL; // LCOV_EXCL_LINE

View File

@@ -104,7 +104,7 @@ CredentialCreate::doApply()
{
auto const subject = ctx_.tx[sfSubject];
auto const credType(ctx_.tx[sfCredentialType]);
Keylet const credentialKey = keylet::credential(subject, account_, credType);
Keylet const credentialKey = keylet::credential(subject, accountID_, credType);
auto const sleCred = std::make_shared<SLE>(credentialKey);
if (!sleCred)
@@ -126,7 +126,7 @@ CredentialCreate::doApply()
sleCred->setFieldU32(sfExpiration, *optExp);
}
auto const sleIssuer = view().peek(keylet::account(account_));
auto const sleIssuer = view().peek(keylet::account(accountID_));
if (!sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -138,15 +138,15 @@ CredentialCreate::doApply()
}
sleCred->setAccountID(sfSubject, subject);
sleCred->setAccountID(sfIssuer, account_);
sleCred->setAccountID(sfIssuer, accountID_);
sleCred->setFieldVL(sfCredentialType, credType);
if (ctx_.tx.isFieldPresent(sfURI))
sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI));
{
auto const page =
view().dirInsert(keylet::ownerDir(account_), credentialKey, describeOwnerDir(account_));
auto const page = view().dirInsert(
keylet::ownerDir(accountID_), credentialKey, describeOwnerDir(accountID_));
JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key)
<< ": " << (page ? "success" : "failure");
if (!page)
@@ -156,7 +156,7 @@ CredentialCreate::doApply()
adjustOwnerCount(view(), sleIssuer, 1, j_);
}
if (subject == account_)
if (subject == accountID_)
{
sleCred->setFieldU32(sfFlags, lsfAccepted);
}

View File

@@ -78,15 +78,15 @@ CredentialDelete::preclaim(PreclaimContext const& ctx)
TER
CredentialDelete::doApply()
{
auto const subject = ctx_.tx[~sfSubject].value_or(account_);
auto const issuer = ctx_.tx[~sfIssuer].value_or(account_);
auto const subject = ctx_.tx[~sfSubject].value_or(accountID_);
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
auto const credType(ctx_.tx[sfCredentialType]);
auto const sleCred = view().peek(keylet::credential(subject, issuer, credType));
if (!sleCred)
return tefINTERNAL; // LCOV_EXCL_LINE
if ((subject != account_) && (issuer != account_) &&
if ((subject != accountID_) && (issuer != accountID_) &&
!checkExpired(*sleCred, ctx_.view().header().parentCloseTime))
{
JLOG(j_.trace()) << "Can't delete non-expired credential.";

View File

@@ -68,12 +68,12 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
TER
DelegateSet::doApply()
{
auto const sleOwner = ctx_.view().peek(keylet::account(account_));
auto const sleOwner = ctx_.view().peek(keylet::account(accountID_));
if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const& authAccount = ctx_.tx[sfAuthorize];
auto const delegateKey = keylet::delegate(account_, authAccount);
auto const delegateKey = keylet::delegate(accountID_, authAccount);
auto sle = ctx_.view().peek(delegateKey);
if (sle)
@@ -101,22 +101,22 @@ DelegateSet::doApply()
return tecINSUFFICIENT_RESERVE;
sle = std::make_shared<SLE>(delegateKey);
sle->setAccountID(sfAccount, account_);
sle->setAccountID(sfAccount, accountID_);
sle->setAccountID(sfAuthorize, authAccount);
sle->setFieldArray(sfPermissions, permissions);
// Add to delegating account's owner directory
auto const page =
ctx_.view().dirInsert(keylet::ownerDir(account_), delegateKey, describeOwnerDir(account_));
auto const page = ctx_.view().dirInsert(
keylet::ownerDir(accountID_), delegateKey, describeOwnerDir(accountID_));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*sle)[sfOwnerNode] = *page;
// Add to authorized account's owner directory so the object can be found
// and cleaned up when the authorized account is deleted.
// Add to authorized account's owner directory so AccountDelete can find
// and clean up inbound delegations when the authorized account is deleted.
auto const destPage = ctx_.view().dirInsert(
keylet::ownerDir(authAccount), delegateKey, describeOwnerDir(authAccount));

View File

@@ -371,7 +371,7 @@ AMMBid::doApply()
// as we go on processing transactions.
Sandbox sb(&ctx_.view());
auto const result = applyBid(ctx_, sb, account_, j_);
auto const result = applyBid(ctx_, sb, accountID_, j_);
if (result.second)
sb.apply(ctx_.rawView());

View File

@@ -385,7 +385,7 @@ AMMCreate::doApply()
// as we go on processing transactions.
Sandbox sb(&ctx_.view());
auto const result = applyCreate(ctx_, sb, account_, j_);
auto const result = applyCreate(ctx_, sb, accountID_, j_);
if (result.second)
sb.apply(ctx_.rawView());

View File

@@ -403,7 +403,7 @@ AMMDeposit::applyGuts(Sandbox& sb)
auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
auto const tfee = (lptAMMBalance == beast::kZero)
? ctx_.tx[~sfTradingFee].value_or(0)
: getTradingFee(ctx_.view(), *ammSle, account_);
: getTradingFee(ctx_.view(), *ammSle, accountID_);
auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx;
@@ -474,7 +474,7 @@ AMMDeposit::applyGuts(Sandbox& sb)
// LP depositing into AMM empty state gets the auction slot
// and the voting
if (lptAMMBalance == beast::kZero)
initializeFeeAuctionVote(sb, ammSle, account_, lptAMMBalance.asset(), tfee);
initializeFeeAuctionVote(sb, ammSle, accountID_, lptAMMBalance.asset(), tfee);
sb.update(ammSle);
}
@@ -519,14 +519,14 @@ AMMDeposit::deposit(
{
auto const& lpIssue = lpTokensDeposit.get<Issue>();
// Adjust the reserve if LP doesn't have LPToken trustline
auto const sle = view.read(keylet::line(account_, lpIssue.account, lpIssue.currency));
if (xrpLiquid(view, account_, !sle, j_) >= depositAmount)
auto const sle = view.read(keylet::line(accountID_, lpIssue.account, lpIssue.currency));
if (xrpLiquid(view, accountID_, !sle, j_) >= depositAmount)
return tesSUCCESS;
}
else if (
accountFunds(
view,
account_,
accountID_,
depositAmount,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
@@ -574,7 +574,7 @@ AMMDeposit::deposit(
}
auto res = accountSend(
view, account_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes);
view, accountID_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes);
if (!isTesSuccess(res))
{
JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << amountDepositActual;
@@ -593,7 +593,12 @@ AMMDeposit::deposit(
}
res = accountSend(
view, account_, ammAccount, *amount2DepositActual, ctx_.journal, WaiveTransferFee::Yes);
view,
accountID_,
ammAccount,
*amount2DepositActual,
ctx_.journal,
WaiveTransferFee::Yes);
if (!isTesSuccess(res))
{
JLOG(ctx_.journal.debug())
@@ -603,7 +608,7 @@ AMMDeposit::deposit(
}
// Deposit LP tokens
res = accountSend(view, ammAccount, account_, lpTokensDepositActual, ctx_.journal);
res = accountSend(view, ammAccount, accountID_, lpTokensDepositActual, ctx_.journal);
if (!isTesSuccess(res))
{
JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens";

View File

@@ -80,14 +80,14 @@ AMMVote::preclaim(PreclaimContext const& ctx)
}
static std::pair<TER, bool>
applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journal j)
applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& accountID, beast::Journal j)
{
auto const feeNew = ctx.tx[sfTradingFee];
auto ammSle = sb.peek(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
if (!ammSle)
return {tecINTERNAL, false};
STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance];
auto const lpTokensNew = ammLPHolds(sb, *ammSle, account, ctx.journal);
auto const lpTokensNew = ammLPHolds(sb, *ammSle, accountID, ctx.journal);
std::optional<STAmount> minTokens;
std::size_t minPos{0};
AccountID minAccount{0};
@@ -108,13 +108,13 @@ applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journ
auto lpTokens = ammLPHolds(sb, *ammSle, entryAccount, ctx.journal);
if (lpTokens == beast::kZero)
{
JLOG(j.debug()) << "AMMVote::applyVote, account " << entryAccount << " is not LP";
JLOG(j.debug()) << "AMMVote::applyVote, accountID " << entryAccount << " is not LP";
continue;
}
auto feeVal = entry[sfTradingFee];
STObject newEntry = STObject::makeInnerObject(sfVoteEntry);
// The account already has the vote entry.
if (entryAccount == account)
if (entryAccount == accountID)
{
lpTokens = lpTokensNew;
feeVal = feeNew;
@@ -156,7 +156,7 @@ applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journ
sfVoteWeight,
static_cast<std::int64_t>(
Number(lpTokensNew) * kVoteWeightScaleFactor / lptAMMBalance));
newEntry.setAccountID(sfAccount, account);
newEntry.setAccountID(sfAccount, accountID);
num += feeNew * lpTokensNew;
den += lpTokensNew;
if (minPos)
@@ -241,7 +241,7 @@ AMMVote::doApply()
// as we go on processing transactions.
Sandbox sb(&ctx_.view());
auto const result = applyVote(ctx_, sb, account_, j_);
auto const result = applyVote(ctx_, sb, accountID_, j_);
if (result.second)
sb.apply(ctx_.rawView());

View File

@@ -329,11 +329,11 @@ AMMWithdraw::applyGuts(Sandbox& sb)
// might not match the LP's trustline balance
if (sb.rules().enabled(fixAMMv1_1))
{
if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokens, ammSle, account_); !res)
if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokens, ammSle, accountID_); !res)
return {res.error(), false};
}
auto const tfee = getTradingFee(ctx_.view(), *ammSle, account_);
auto const tfee = getTradingFee(ctx_.view(), *ammSle, accountID_);
auto const expected = ammHolds(
sb,
@@ -458,7 +458,7 @@ AMMWithdraw::withdraw(
view,
ammSle,
ammAccount,
account_,
accountID_,
amountBalance,
amountWithdraw,
amount2Withdraw,
@@ -748,7 +748,7 @@ AMMWithdraw::equalWithdrawTokens(
std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = equalWithdrawTokens(
view,
ammSle,
account_,
accountID_,
ammAccount,
amountBalance,
amount2Balance,

View File

@@ -55,11 +55,11 @@ OfferCancel::doApply()
{
auto const offerSequence = ctx_.tx[sfOfferSequence];
auto const sle = view().read(keylet::account(account_));
auto const sle = view().read(keylet::account(accountID_));
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
if (auto sleOffer = view().peek(keylet::offer(account_, offerSequence)))
if (auto sleOffer = view().peek(keylet::offer(accountID_, offerSequence)))
{
JLOG(j_.debug()) << "Trying to cancel offer #" << offerSequence;
return offerDelete(view(), sleOffer, ctx_.registry.get().getJournal("View"));

View File

@@ -341,14 +341,14 @@ OfferCreate::flowCross(
// below the reserve) so we check this case again.
STAmount const inStartBalance = accountFunds(
psb,
account_,
accountID_,
takerAmount.in,
FreezeHandling::ZeroIfFrozen,
AuthHandling::ZeroIfUnauthorized,
j_);
// Allow unfunded MPT issuer
auto const disallowUnfunded =
!inStartBalance.holds<MPTIssue>() || inStartBalance.getIssuer() != account_;
!inStartBalance.holds<MPTIssue>() || inStartBalance.getIssuer() != accountID_;
if (disallowUnfunded && inStartBalance <= beast::kZero)
{
// The account balance can't cover even part of the offer.
@@ -361,7 +361,7 @@ OfferCreate::flowCross(
// offer taker. Set sendMax to allow for the gateway's cut.
Rate gatewayXferRate{QUALITY_ONE};
STAmount sendMax = takerAmount.in;
if (!sendMax.native() && (account_ != sendMax.getIssuer()))
if (!sendMax.native() && (accountID_ != sendMax.getIssuer()))
{
gatewayXferRate = transferRate(psb, sendMax);
if (gatewayXferRate.value != QUALITY_ONE)
@@ -428,8 +428,8 @@ OfferCreate::flowCross(
auto const result = flow(
psb,
deliver,
account_,
account_,
accountID_,
accountID_,
paths,
true, // default path
!ctx_.tx.isFlag(tfFillOrKill), // partial payment
@@ -455,7 +455,7 @@ OfferCreate::flowCross(
{
STAmount const takerInBalance = accountFunds(
psb,
account_,
accountID_,
takerAmount.in,
FreezeHandling::ZeroIfFrozen,
AuthHandling::ZeroIfUnauthorized,
@@ -619,7 +619,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
// Process a cancellation request that's passed along with an offer.
if (cancelSequence)
{
auto const sleCancel = sb.peek(keylet::offer(account_, *cancelSequence));
auto const sleCancel = sb.peek(keylet::offer(accountID_, *cancelSequence));
// It's not an error to not find the offer to cancel: it might have
// been consumed or removed. If it is found, however, it's an error
@@ -810,7 +810,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
return {tesSUCCESS, true};
}
auto const sleCreator = sb.peek(keylet::account(account_));
auto const sleCreator = sb.peek(keylet::account(accountID_));
if (!sleCreator)
return {tefINTERNAL, false};
@@ -836,11 +836,11 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
}
// We need to place the remainder of the offer into its order book.
auto const offerIndex = keylet::offer(account_, offerSequence);
auto const offerIndex = keylet::offer(accountID_, offerSequence);
// Add offer to owner's directory.
auto const ownerNode =
sb.dirInsert(keylet::ownerDir(account_), offerIndex, describeOwnerDir(account_));
sb.dirInsert(keylet::ownerDir(accountID_), offerIndex, describeOwnerDir(accountID_));
if (!ownerNode)
{
@@ -905,7 +905,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
}
auto sleOffer = std::make_shared<SLE>(offerIndex);
sleOffer->setAccountID(sfAccount, account_);
sleOffer->setAccountID(sfAccount, accountID_);
sleOffer->setFieldU32(sfSequence, offerSequence);
sleOffer->setFieldH256(sfBookDirectory, dir.key);
sleOffer->setFieldAmount(sfTakerPays, saTakerPays);

View File

@@ -67,7 +67,7 @@ DIDDelete::deleteSLE(
TER
DIDDelete::doApply()
{
return deleteSLE(ctx_, keylet::did(account_), account_);
return deleteSLE(ctx_, keylet::did(accountID_), accountID_);
}
void

View File

@@ -99,7 +99,7 @@ TER
DIDSet::doApply()
{
// Edit ledger object if it already exists
Keylet const didKeylet = keylet::did(account_);
Keylet const didKeylet = keylet::did(accountID_);
if (auto const sleDID = ctx_.view().peek(didKeylet))
{
auto update = [&](auto const& sField) {
@@ -130,7 +130,7 @@ DIDSet::doApply()
// Create new ledger object otherwise
auto const sleDID = std::make_shared<SLE>(didKeylet);
(*sleDID)[sfAccount] = account_;
(*sleDID)[sfAccount] = accountID_;
auto set = [&](auto const& sField) {
if (auto const field = ctx_.tx[~sField]; field && !field->empty())
@@ -146,7 +146,7 @@ DIDSet::doApply()
return tecEMPTY_DID;
}
return addSLE(ctx_, sleDID, account_);
return addSLE(ctx_, sleDID, accountID_);
}
void

View File

@@ -178,13 +178,13 @@ EscrowCancel::doApply()
return temDISABLED; // LCOV_EXCL_LINE
auto const issuer = amount.getIssuer();
bool const createAsset = account == account_;
bool const createAsset = account == accountID_;
if (auto const ret = std::visit(
[&]<typename T>(T const&) {
return escrowUnlockApplyHelper<T>(
ctx_.view(),
kParityRate,
slep,
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : slep,
preFeeBalance_,
amount,
issuer,

View File

@@ -417,7 +417,7 @@ EscrowCreate::doApply()
if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter]))
return tecNO_PERMISSION;
auto const sle = ctx_.view().peek(keylet::account(account_));
auto const sle = ctx_.view().peek(keylet::account(accountID_));
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -448,10 +448,10 @@ EscrowCreate::doApply()
// Create escrow in ledger. Note that we use the value from the
// sequence or ticket. For more explanation see comments in SeqProxy.h.
Keylet const escrowKeylet = keylet::escrow(account_, ctx_.tx.getSeqValue());
Keylet const escrowKeylet = keylet::escrow(accountID_, ctx_.tx.getSeqValue());
auto const slep = std::make_shared<SLE>(escrowKeylet);
(*slep)[sfAmount] = amount;
(*slep)[sfAccount] = account_;
(*slep)[sfAccount] = accountID_;
(*slep)[~sfCondition] = ctx_.tx[~sfCondition];
(*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
(*slep)[sfDestination] = ctx_.tx[sfDestination];
@@ -476,7 +476,7 @@ EscrowCreate::doApply()
// Add escrow to sender's owner directory
{
auto page = ctx_.view().dirInsert(
keylet::ownerDir(account_), escrowKeylet, describeOwnerDir(account_));
keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*slep)[sfOwnerNode] = *page;
@@ -484,7 +484,7 @@ EscrowCreate::doApply()
// If it's not a self-send, add escrow to recipient's owner directory.
AccountID const dest = ctx_.tx[sfDestination];
if (dest != account_)
if (dest != accountID_)
{
auto page =
ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
@@ -497,7 +497,7 @@ EscrowCreate::doApply()
// track the total locked balance. For MPT, this isn't necessary because the
// locked balance is already stored directly in the MPTokenIssuance object.
AccountID const issuer = amount.getIssuer();
if (!isXRP(amount) && issuer != account_ && issuer != dest && !amount.holds<MPTIssue>())
if (!isXRP(amount) && issuer != accountID_ && issuer != dest && !amount.holds<MPTIssue>())
{
auto page =
ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
@@ -515,7 +515,7 @@ EscrowCreate::doApply()
{
if (auto const ret = std::visit(
[&]<typename T>(T const&) {
return escrowLockApplyHelper<T>(ctx_.view(), issuer, account_, amount, j_);
return escrowLockApplyHelper<T>(ctx_.view(), issuer, accountID_, amount, j_);
},
amount.asset().value());
!isTesSuccess(ret))

View File

@@ -310,7 +310,8 @@ EscrowFinish::doApply()
if (!sled)
return tecNO_DST;
if (auto err = verifyDepositPreauth(ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
if (auto err =
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal);
!isTesSuccess(err))
return err;
@@ -355,7 +356,7 @@ EscrowFinish::doApply()
? xrpl::Rate(slep->getFieldU32(sfTransferRate))
: kParityRate;
auto const issuer = amount.getIssuer();
bool const createAsset = destID == account_;
bool const createAsset = destID == accountID_;
if (auto const ret = std::visit(
[&]<typename T>(T const&) {
return escrowUnlockApplyHelper<T>(

View File

@@ -121,7 +121,8 @@ LoanBrokerCoverDeposit::doApply()
auto const brokerPseudoID = broker->at(sfAccount);
// Transfer assets from depositor to pseudo-account.
if (auto ter = accountSend(view(), account_, brokerPseudoID, amount, j_, WaiveTransferFee::Yes))
if (auto ter =
accountSend(view(), accountID_, brokerPseudoID, amount, j_, WaiveTransferFee::Yes))
return ter;
// Increase the LoanBroker's CoverAvailable by Amount

View File

@@ -163,7 +163,7 @@ LoanBrokerCoverWithdraw::doApply()
auto const brokerID = tx[sfLoanBrokerID];
auto const amount = tx[sfAmount];
auto const dstAcct = tx[~sfDestination].value_or(account_);
auto const dstAcct = tx[~sfDestination].value_or(accountID_);
auto broker = view().peek(keylet::loanbroker(brokerID));
if (!broker)
@@ -183,7 +183,7 @@ LoanBrokerCoverWithdraw::doApply()
associateAsset(*broker, vaultAsset);
return doWithdraw(view(), tx, account_, dstAcct, brokerPseudoID, preFeeBalance_, amount, j_);
return doWithdraw(view(), tx, accountID_, dstAcct, brokerPseudoID, preFeeBalance_, amount, j_);
}
void

View File

@@ -130,7 +130,7 @@ LoanBrokerDelete::doApply()
auto const brokerPseudoID = broker->at(sfAccount);
if (!view().dirRemove(
keylet::ownerDir(account_), broker->at(sfOwnerNode), broker->key(), false))
keylet::ownerDir(accountID_), broker->at(sfOwnerNode), broker->key(), false))
{
return tefBAD_LEDGER; // LCOV_EXCL_LINE
}
@@ -143,7 +143,7 @@ LoanBrokerDelete::doApply()
{
auto const coverAvailable = STAmount{vaultAsset, broker->at(sfCoverAvailable)};
if (auto const ter = accountSend(
view(), brokerPseudoID, account_, coverAvailable, j_, WaiveTransferFee::Yes))
view(), brokerPseudoID, accountID_, coverAvailable, j_, WaiveTransferFee::Yes))
return ter;
}
@@ -177,7 +177,7 @@ LoanBrokerDelete::doApply()
view().erase(broker);
{
auto owner = view().peek(keylet::account(account_));
auto owner = view().peek(keylet::account(accountID_));
if (!owner)
return tefBAD_LEDGER; // LCOV_EXCL_LINE

View File

@@ -220,7 +220,7 @@ LoanBrokerSet::doApply()
auto const vaultAsset = sleVault->at(sfAsset);
auto const sequence = tx.getSeqValue();
auto owner = view.peek(keylet::account(account_));
auto owner = view.peek(keylet::account(accountID_));
if (!owner)
{
// This should be impossible
@@ -229,9 +229,9 @@ LoanBrokerSet::doApply()
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
auto broker = std::make_shared<SLE>(keylet::loanbroker(account_, sequence));
auto broker = std::make_shared<SLE>(keylet::loanbroker(accountID_, sequence));
if (auto const ter = dirLink(view, account_, broker))
if (auto const ter = dirLink(view, accountID_, broker))
return ter; // LCOV_EXCL_LINE
if (auto const ter = dirLink(view, vaultPseudoID, broker, sfVaultNode))
return ter; // LCOV_EXCL_LINE
@@ -255,7 +255,7 @@ LoanBrokerSet::doApply()
// Initialize data fields:
broker->at(sfSequence) = sequence;
broker->at(sfVaultID) = vaultID;
broker->at(sfOwner) = account_;
broker->at(sfOwner) = accountID_;
broker->at(sfAccount) = pseudoId;
// The LoanSequence indexes loans created by this broker, starting at 1
broker->at(sfLoanSequence) = 1;

View File

@@ -99,7 +99,7 @@ LoanPay::calculateBaseFee(ReadView const& view, STTx const& tx)
if (loanSle->at(sfPaymentRemaining) <= kLoanPaymentsPerFeeIncrement)
{
// If there are fewer than loanPaymentsPerFeeIncrement payments left to
// If there are fewer than kLoanPaymentsPerFeeIncrement payments left to
// pay, we can skip the computations.
return normalCost;
}
@@ -581,13 +581,13 @@ LoanPay::doApply()
// These three values are used to check that funds are conserved after the transfers
auto const accountBalanceBefore = accountHolds(
view,
account_,
accountID_,
asset,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
j_,
SpendableHandling::FullBalance);
auto const vaultBalanceBefore = account_ == vaultPseudoAccount
auto const vaultBalanceBefore = accountID_ == vaultPseudoAccount
? STAmount{asset, 0}
: accountHolds(
view,
@@ -597,15 +597,16 @@ LoanPay::doApply()
AuthHandling::IgnoreAuth,
j_,
SpendableHandling::FullBalance);
auto const brokerBalanceBefore = account_ == brokerPayee ? STAmount{asset, 0}
: accountHolds(
view,
brokerPayee,
asset,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
j_,
SpendableHandling::FullBalance);
auto const brokerBalanceBefore = accountID_ == brokerPayee
? STAmount{asset, 0}
: accountHolds(
view,
brokerPayee,
asset,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
j_,
SpendableHandling::FullBalance);
if (totalPaidToVaultRounded != beast::kZero)
{
@@ -615,7 +616,7 @@ LoanPay::doApply()
if (totalPaidToBroker != beast::kZero)
{
if (brokerPayee == account_)
if (brokerPayee == accountID_)
{
// The broker may have deleted their holding. Recreate it if needed
if (auto const ter = addEmptyHolding(
@@ -633,7 +634,7 @@ LoanPay::doApply()
if (auto const ter = accountSendMulti(
view,
account_,
accountID_,
asset,
{{vaultPseudoAccount, totalPaidToVaultRounded}, {brokerPayee, totalPaidToBroker}},
j_,
@@ -659,13 +660,13 @@ LoanPay::doApply()
// Check that funds are conserved
auto const accountBalanceAfter = accountHolds(
view,
account_,
accountID_,
asset,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
j_,
SpendableHandling::FullBalance);
auto const vaultBalanceAfter = account_ == vaultPseudoAccount
auto const vaultBalanceAfter = accountID_ == vaultPseudoAccount
? STAmount{asset, 0}
: accountHolds(
view,
@@ -675,15 +676,15 @@ LoanPay::doApply()
AuthHandling::IgnoreAuth,
j_,
SpendableHandling::FullBalance);
auto const brokerBalanceAfter = account_ == brokerPayee ? STAmount{asset, 0}
: accountHolds(
view,
brokerPayee,
asset,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
j_,
SpendableHandling::FullBalance);
auto const brokerBalanceAfter = accountID_ == brokerPayee ? STAmount{asset, 0}
: accountHolds(
view,
brokerPayee,
asset,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
j_,
SpendableHandling::FullBalance);
auto const balanceScale = [&]() {
// Find a reasonable scale to use for the balance comparisons.
//
@@ -800,7 +801,7 @@ LoanPay::doApply()
goodRounding, "xrpl::LoanPay::doApply", "funds are conserved (with rounding)");
XRPL_ASSERT_PARTS(
accountBalanceAfter < accountBalanceBefore || account_ == asset.getIssuer(),
accountBalanceAfter < accountBalanceBefore || accountID_ == asset.getIssuer(),
"xrpl::LoanPay::doApply",
"account balance decreased");
XRPL_ASSERT_PARTS(

View File

@@ -388,7 +388,7 @@ LoanSet::doApply()
Asset const vaultAsset = vaultSle->at(sfAsset);
auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner);
auto const borrower = counterparty == brokerOwner ? account_ : counterparty;
auto const borrower = counterparty == brokerOwner ? accountID_ : counterparty;
auto const borrowerSle = view.peek(keylet::account(borrower));
if (!borrowerSle)
{
@@ -508,7 +508,7 @@ LoanSet::doApply()
{
auto const ownerCount = borrowerSle->at(sfOwnerCount);
auto const balance =
account_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp();
accountID_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp();
if (balance < view.fees().accountReserve(ownerCount))
return tecINSUFFICIENT_RESERVE;
}
@@ -520,7 +520,7 @@ LoanSet::doApply()
// Create a holding for the borrower if one does not already exist.
XRPL_ASSERT_PARTS(
borrower == account_ || borrower == counterparty,
borrower == accountID_ || borrower == counterparty,
"xrpl::LoanSet::doApply",
"borrower signed transaction");
if (auto const ter = addEmptyHolding(
@@ -542,7 +542,7 @@ LoanSet::doApply()
// Create the holding if it doesn't already exist (necessary for MPTs).
// The owner may have deleted their MPT / line at some point.
XRPL_ASSERT_PARTS(
brokerOwner == account_ || brokerOwner == counterparty,
brokerOwner == accountID_ || brokerOwner == counterparty,
"xrpl::LoanSet::doApply",
"broker owner signed transaction");

View File

@@ -407,8 +407,8 @@ NFTokenAcceptOffer::acceptOffer(std::shared_ptr<SLE> const& offer)
{
bool const isSell = offer->isFlag(lsfSellNFToken);
AccountID const owner = (*offer)[sfOwner];
AccountID const& seller = isSell ? owner : account_;
AccountID const& buyer = isSell ? account_ : owner;
AccountID const& seller = isSell ? owner : accountID_;
AccountID const& buyer = isSell ? accountID_ : owner;
auto const nftokenID = (*offer)[sfNFTokenID];
@@ -528,7 +528,7 @@ NFTokenAcceptOffer::doApply()
// Send the broker the amount they requested.
if (auto const cut = ctx_.tx[~sfNFTokenBrokerFee]; cut && cut.value() != beast::kZero)
{
if (auto const r = pay(buyer, account_, cut.value()); !isTesSuccess(r))
if (auto const r = pay(buyer, accountID_, cut.value()); !isTesSuccess(r))
return r;
amount -= cut.value();

View File

@@ -222,7 +222,7 @@ NFTokenMint::preclaim(PreclaimContext const& ctx)
TER
NFTokenMint::doApply()
{
auto const issuer = ctx_.tx[~sfIssuer].value_or(account_);
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
auto const tokenSeq = [this, &issuer]() -> Expected<std::uint32_t, TER> {
auto const root = view().peek(keylet::account(issuer));
@@ -279,7 +279,7 @@ NFTokenMint::doApply()
return (tokenSeq.error());
std::uint32_t const ownerCountBefore =
view().read(keylet::account(account_))->getFieldU32(sfOwnerCount);
view().read(keylet::account(accountID_))->getFieldU32(sfOwnerCount);
// Assemble the new NFToken.
SOTemplate const* nfTokenTemplate =
@@ -305,7 +305,7 @@ NFTokenMint::doApply()
object.setFieldVL(sfURI, *uri);
});
if (TER const ret = nft::insertToken(ctx_.view(), account_, std::move(newToken));
if (TER const ret = nft::insertToken(ctx_.view(), accountID_, std::move(newToken));
!isTesSuccess(ret))
return ret;
@@ -333,7 +333,7 @@ NFTokenMint::doApply()
// requiring the reserve to be met each time. The reserve is
// only managed when a new NFT page or sell offer is added.
if (auto const ownerCountAfter =
view().read(keylet::account(account_))->getFieldU32(sfOwnerCount);
view().read(keylet::account(accountID_))->getFieldU32(sfOwnerCount);
ownerCountAfter > ownerCountBefore)
{
if (auto const reserve = view().fees().accountReserve(ownerCountAfter);

View File

@@ -82,8 +82,8 @@ OracleDelete::deleteOracle(
TER
OracleDelete::doApply()
{
if (auto sle = ctx_.view().peek(keylet::oracle(account_, ctx_.tx[sfOracleDocumentID])))
return deleteOracle(ctx_.view(), sle, account_, j_);
if (auto sle = ctx_.view().peek(keylet::oracle(accountID_, ctx_.tx[sfOracleDocumentID])))
return deleteOracle(ctx_.view(), sle, accountID_, j_);
return tecINTERNAL; // LCOV_EXCL_LINE
}

View File

@@ -201,7 +201,7 @@ setPriceDataInnerObjTemplate(STObject& obj)
TER
OracleSet::doApply()
{
auto const oracleID = keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]);
auto const oracleID = keylet::oracle(accountID_, ctx_.tx[sfOracleDocumentID]);
auto populatePriceData = [](STObject& priceData, STObject const& entry) {
setPriceDataInnerObjTemplate(priceData);
@@ -311,7 +311,7 @@ OracleSet::doApply()
sle->setFieldU32(sfLastUpdateTime, ctx_.tx[sfLastUpdateTime]);
auto page = ctx_.view().dirInsert(
keylet::ownerDir(account_), sle->key(), describeOwnerDir(account_));
keylet::ownerDir(accountID_), sle->key(), describeOwnerDir(accountID_));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE

View File

@@ -153,7 +153,7 @@ DepositPreauth::doApply()
{
if (ctx_.tx.isFieldPresent(sfAuthorize))
{
auto const sleOwner = view().peek(keylet::account(account_));
auto const sleOwner = view().peek(keylet::account(accountID_));
if (!sleOwner)
return {tefINTERNAL};
@@ -171,15 +171,15 @@ DepositPreauth::doApply()
// Preclaim already verified that the Preauth entry does not yet exist.
// Create and populate the Preauth entry.
AccountID const auth{ctx_.tx[sfAuthorize]};
Keylet const preauthKeylet = keylet::depositPreauth(account_, auth);
Keylet const preauthKeylet = keylet::depositPreauth(accountID_, auth);
auto slePreauth = std::make_shared<SLE>(preauthKeylet);
slePreauth->setAccountID(sfAccount, account_);
slePreauth->setAccountID(sfAccount, accountID_);
slePreauth->setAccountID(sfAuthorize, auth);
view().insert(slePreauth);
auto const page =
view().dirInsert(keylet::ownerDir(account_), preauthKeylet, describeOwnerDir(account_));
auto const page = view().dirInsert(
keylet::ownerDir(accountID_), preauthKeylet, describeOwnerDir(accountID_));
JLOG(j_.trace()) << "Adding DepositPreauth to owner directory "
<< to_string(preauthKeylet.key) << ": " << (page ? "success" : "failure");
@@ -194,13 +194,13 @@ DepositPreauth::doApply()
}
else if (ctx_.tx.isFieldPresent(sfUnauthorize))
{
auto const preauth = keylet::depositPreauth(account_, ctx_.tx[sfUnauthorize]);
auto const preauth = keylet::depositPreauth(accountID_, ctx_.tx[sfUnauthorize]);
return DepositPreauth::removeFromLedger(view(), preauth.key, j_);
}
else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
{
auto const sleOwner = view().peek(keylet::account(account_));
auto const sleOwner = view().peek(keylet::account(accountID_));
if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -229,18 +229,18 @@ DepositPreauth::doApply()
sortedLE.pushBack(std::move(cred));
}
Keylet const preauthKey = keylet::depositPreauth(account_, sortedTX);
Keylet const preauthKey = keylet::depositPreauth(accountID_, sortedTX);
auto slePreauth = std::make_shared<SLE>(preauthKey);
if (!slePreauth)
return tefINTERNAL; // LCOV_EXCL_LINE
slePreauth->setAccountID(sfAccount, account_);
slePreauth->setAccountID(sfAccount, accountID_);
slePreauth->peekFieldArray(sfAuthorizeCredentials) = std::move(sortedLE);
view().insert(slePreauth);
auto const page =
view().dirInsert(keylet::ownerDir(account_), preauthKey, describeOwnerDir(account_));
auto const page = view().dirInsert(
keylet::ownerDir(accountID_), preauthKey, describeOwnerDir(accountID_));
JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKey.key)
<< ": " << (page ? "success" : "failure");
@@ -256,7 +256,7 @@ DepositPreauth::doApply()
else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
{
auto const preauthKey = keylet::depositPreauth(
account_, credentials::makeSorted(ctx_.tx.getFieldArray(sfUnauthorizeCredentials)));
accountID_, credentials::makeSorted(ctx_.tx.getFieldArray(sfUnauthorizeCredentials)));
return DepositPreauth::removeFromLedger(view(), preauthKey.key, j_);
}

View File

@@ -410,7 +410,7 @@ Payment::doApply()
AccountID const dstAccountID(ctx_.tx.getAccountID(sfDestination));
STAmount const dstAmount(ctx_.tx.getFieldAmount(sfAmount));
bool const isDstMPT = dstAmount.holds<MPTIssue>();
STAmount const maxSourceAmount = getMaxSourceAmount(account_, dstAmount, sendMax);
STAmount const maxSourceAmount = getMaxSourceAmount(accountID_, dstAmount, sendMax);
JLOG(j_.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText()
<< " dstAmount=" << dstAmount.getFullText();
@@ -453,7 +453,7 @@ Payment::doApply()
// 2. If Account is deposit preauthorized by destination.
if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal);
ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal);
!isTesSuccess(err))
return err;
@@ -472,7 +472,7 @@ Payment::doApply()
maxSourceAmount,
dstAmount,
dstAccountID,
account_,
accountID_,
ctx_.tx.getFieldPathSet(sfPaths),
ctx_.tx[~sfDomainID],
ctx_.registry,
@@ -512,18 +512,18 @@ Payment::doApply()
JLOG(j_.trace()) << " dstAmount=" << dstAmount.getFullText();
auto const& mptIssue = dstAmount.get<MPTIssue>();
if (auto const ter = requireAuth(view(), mptIssue, account_); !isTesSuccess(ter))
if (auto const ter = requireAuth(view(), mptIssue, accountID_); !isTesSuccess(ter))
return ter;
if (auto const ter = requireAuth(view(), mptIssue, dstAccountID); !isTesSuccess(ter))
return ter;
if (auto const ter = canTransfer(view(), mptIssue, account_, dstAccountID);
if (auto const ter = canTransfer(view(), mptIssue, accountID_, dstAccountID);
!isTesSuccess(ter))
return ter;
if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal);
ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal);
!isTesSuccess(err))
return err;
@@ -532,13 +532,13 @@ Payment::doApply()
// Transfer rate
Rate rate{QUALITY_ONE};
// Payment between the holders
if (account_ != issuer && dstAccountID != issuer)
if (accountID_ != issuer && dstAccountID != issuer)
{
// If globally/individually locked then
// - can't send between holders
// - holder can send back to issuer
// - issuer can send to holder
if (isAnyFrozen(view(), {account_, dstAccountID}, mptIssue))
if (isAnyFrozen(view(), {accountID_, dstAccountID}, mptIssue))
return tecLOCKED;
// Get the rate for a payment between the holders.
@@ -566,7 +566,7 @@ Payment::doApply()
return tecPATH_PARTIAL;
PaymentSandbox pv(&view());
auto res = accountSend(pv, account_, dstAccountID, amountDeliver, ctx_.journal);
auto res = accountSend(pv, accountID_, dstAccountID, amountDeliver, ctx_.journal);
if (isTesSuccess(res))
{
pv.apply(ctx_.rawView());
@@ -589,7 +589,7 @@ Payment::doApply()
// Direct XRP payment.
auto const sleSrc = view().peek(keylet::account(account_));
auto const sleSrc = view().peek(keylet::account(accountID_));
if (!sleSrc)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -601,10 +601,10 @@ Payment::doApply()
auto const reserve = view().fees().accountReserve(ownerCount);
// In a delegated payment, the fee payer is the delegated account,
// not the source account (account_).
bool const accountIsPayer = (ctx_.tx.getFeePayer() == account_);
// not the source account (accountID_).
bool const accountIsPayer = (ctx_.tx.getFeePayer() == accountID_);
// preFeeBalance_ is the balance on the source account (account_) BEFORE the fees
// preFeeBalance_ is the balance on the source account (accountID_) BEFORE the fees
// were charged. If source account is the fee payer, it must also cover the fee.
// The final spend may use the reserve to cover fees.
auto const minRequiredFunds =
@@ -656,7 +656,7 @@ Payment::doApply()
if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve)
{
if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), account_, dstAccountID, sleDst, ctx_.journal);
ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal);
!isTesSuccess(err))
return err;
}

View File

@@ -55,7 +55,7 @@ PermissionedDomainDelete::doApply()
auto const slePd = view().peek(keylet::permissionedDomain(ctx_.tx.at(sfDomainID)));
auto const page = (*slePd)[sfOwnerNode];
if (!view().dirRemove(keylet::ownerDir(account_), page, slePd->key(), true))
if (!view().dirRemove(keylet::ownerDir(accountID_), page, slePd->key(), true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete permissioned domain directory entry.";
@@ -63,7 +63,7 @@ PermissionedDomainDelete::doApply()
// LCOV_EXCL_STOP
}
auto const ownerSle = view().peek(keylet::account(account_));
auto const ownerSle = view().peek(keylet::account(accountID_));
XRPL_ASSERT(
ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0,
"xrpl::PermissionedDomainDelete::doApply : nonzero owner count");

View File

@@ -77,7 +77,7 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx)
TER
PermissionedDomainSet::doApply()
{
auto const ownerSle = view().peek(keylet::account(account_));
auto const ownerSle = view().peek(keylet::account(accountID_));
if (!ownerSle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -112,14 +112,14 @@ PermissionedDomainSet::doApply()
bool const fixEnabled = view().rules().enabled(fixCleanup3_1_3);
auto const seq = fixEnabled ? ctx_.tx.getSeqValue() : ctx_.tx.getFieldU32(sfSequence);
Keylet const pdKeylet = keylet::permissionedDomain(account_, seq);
Keylet const pdKeylet = keylet::permissionedDomain(accountID_, seq);
auto slePd = std::make_shared<SLE>(pdKeylet);
slePd->setAccountID(sfOwner, account_);
slePd->setAccountID(sfOwner, accountID_);
slePd->setFieldU32(sfSequence, seq);
slePd->peekFieldArray(sfAcceptedCredentials) = std::move(sortedLE);
auto const page =
view().dirInsert(keylet::ownerDir(account_), pdKeylet, describeOwnerDir(account_));
view().dirInsert(keylet::ownerDir(accountID_), pdKeylet, describeOwnerDir(accountID_));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE

View File

@@ -154,7 +154,7 @@ Change::doApply()
void
Change::preCompute()
{
XRPL_ASSERT(account_ == beast::kZero, "xrpl::Change::preCompute : zero account");
XRPL_ASSERT(accountID_ == beast::kZero, "xrpl::Change::preCompute : zero account");
}
TER

View File

@@ -67,7 +67,7 @@ TicketCreate::preclaim(PreclaimContext const& ctx)
TER
TicketCreate::doApply()
{
SLE::pointer const sleAccountRoot = view().peek(keylet::account(account_));
SLE::pointer const sleAccountRoot = view().peek(keylet::account(accountID_));
if (!sleAccountRoot)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -100,15 +100,15 @@ TicketCreate::doApply()
for (std::uint32_t i = 0; i < ticketCount; ++i)
{
std::uint32_t const curTicketSeq = firstTicketSeq + i;
Keylet const ticketKeylet = keylet::kTicket(account_, curTicketSeq);
Keylet const ticketKeylet = keylet::kTicket(accountID_, curTicketSeq);
SLE::pointer const sleTicket = std::make_shared<SLE>(ticketKeylet);
sleTicket->setAccountID(sfAccount, account_);
sleTicket->setAccountID(sfAccount, accountID_);
sleTicket->setFieldU32(sfTicketSequence, curTicketSeq);
view().insert(sleTicket);
auto const page =
view().dirInsert(keylet::ownerDir(account_), ticketKeylet, describeOwnerDir(account_));
auto const page = view().dirInsert(
keylet::ownerDir(accountID_), ticketKeylet, describeOwnerDir(accountID_));
JLOG(j_.trace()) << "Creating ticket " << to_string(ticketKeylet.key) << ": "
<< (page ? "success" : "failure");

View File

@@ -148,7 +148,7 @@ MPTokenAuthorize::doApply()
ctx_.view(),
preFeeBalance_,
tx[sfMPTokenIssuanceID],
account_,
accountID_,
ctx_.journal,
tx.getFlags(),
tx[~sfHolder]);

View File

@@ -159,7 +159,7 @@ MPTokenIssuanceCreate::doApply()
j_,
{
.priorBalance = preFeeBalance_,
.account = account_,
.account = accountID_,
.sequence = tx.getSeqValue(),
.flags = tx.getFlags(),
.maxAmount = tx[~sfMaximumAmount],

View File

@@ -45,15 +45,15 @@ TER
MPTokenIssuanceDestroy::doApply()
{
auto const mpt = view().peek(keylet::mptIssuance(ctx_.tx[sfMPTokenIssuanceID]));
if (account_ != mpt->getAccountID(sfIssuer))
if (accountID_ != mpt->getAccountID(sfIssuer))
return tecINTERNAL; // LCOV_EXCL_LINE
if (!view().dirRemove(keylet::ownerDir(account_), (*mpt)[sfOwnerNode], mpt->key(), false))
if (!view().dirRemove(keylet::ownerDir(accountID_), (*mpt)[sfOwnerNode], mpt->key(), false))
return tefBAD_LEDGER; // LCOV_EXCL_LINE
view().erase(mpt);
adjustOwnerCount(view(), view().peek(keylet::account(account_)), -1, j_);
adjustOwnerCount(view(), view().peek(keylet::account(accountID_)), -1, j_);
return tesSUCCESS;
}

View File

@@ -336,9 +336,9 @@ TrustSet::doApply()
AccountID const uDstAccountID(saLimitAmount.getIssuer());
// true, if current is high account.
bool const bHigh = account_ > uDstAccountID;
bool const bHigh = accountID_ > uDstAccountID;
auto const sle = view().peek(keylet::account(account_));
auto const sle = view().peek(keylet::account(accountID_));
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -391,10 +391,10 @@ TrustSet::doApply()
}
STAmount saLimitAllow = saLimitAmount;
saLimitAllow.get<Issue>().account = account_;
saLimitAllow.get<Issue>().account = accountID_;
SLE::pointer const sleRippleState =
view().peek(keylet::line(account_, uDstAccountID, currency));
view().peek(keylet::line(accountID_, uDstAccountID, currency));
if (sleRippleState)
{
@@ -406,8 +406,8 @@ TrustSet::doApply()
std::uint32_t uLowQualityOut = 0;
std::uint32_t uHighQualityIn = 0;
std::uint32_t uHighQualityOut = 0;
auto const& uLowAccountID = !bHigh ? account_ : uDstAccountID;
auto const& uHighAccountID = bHigh ? account_ : uDstAccountID;
auto const& uLowAccountID = !bHigh ? accountID_ : uDstAccountID;
auto const& uHighAccountID = bHigh ? accountID_ : uDstAccountID;
SLE::ref sleLowAccount = !bHigh ? sle : sleDst;
SLE::ref sleHighAccount = bHigh ? sle : sleDst;
@@ -642,7 +642,7 @@ TrustSet::doApply()
// Zero balance in currency.
STAmount const saBalance(Issue{currency, noAccount()});
auto const k = keylet::line(account_, uDstAccountID, currency);
auto const k = keylet::line(accountID_, uDstAccountID, currency);
JLOG(j_.trace()) << "doTrustSet: Creating ripple line: " << to_string(k.key);
@@ -650,7 +650,7 @@ TrustSet::doApply()
terResult = trustCreate(
view(),
bHigh,
account_,
accountID_,
uDstAccountID,
k.key,
sle,

View File

@@ -349,7 +349,7 @@ VaultClawback::doApply()
MPTIssue const share{mptIssuanceID};
Asset const vaultAsset = vault->at(sfAsset);
STAmount const amount = clawbackAmount(vault, tx[~sfAmount], account_);
STAmount const amount = clawbackAmount(vault, tx[~sfAmount], accountID_);
auto assetsAvailable = vault->at(sfAssetsAvailable);
auto assetsTotal = vault->at(sfAssetsTotal);
@@ -364,7 +364,7 @@ VaultClawback::doApply()
STAmount assetsRecovered = {vault->at(sfAsset)};
// The Owner is burning shares
if (account_ == vault->at(sfOwner) && amount.asset() == share)
if (accountID_ == vault->at(sfOwner) && amount.asset() == share)
{
sharesDestroyed = accountHolds(
view(), holder, share, FreezeHandling::IgnoreFreeze, AuthHandling::IgnoreAuth, j_);
@@ -426,7 +426,7 @@ VaultClawback::doApply()
{
// Transfer assets from vault to issuer.
if (auto const ter = accountSend(
view(), vaultAccount, account_, assetsRecovered, j_, WaiveTransferFee::Yes);
view(), vaultAccount, accountID_, assetsRecovered, j_, WaiveTransferFee::Yes);
!isTesSuccess(ter))
return ter;

View File

@@ -145,13 +145,13 @@ VaultCreate::doApply()
auto const& tx = ctx_.tx;
auto const sequence = tx.getSeqValue();
auto const owner = view().peek(keylet::account(account_));
auto const owner = view().peek(keylet::account(accountID_));
if (owner == nullptr)
return tefINTERNAL; // LCOV_EXCL_LINE
auto vault = std::make_shared<SLE>(keylet::vault(account_, sequence));
auto vault = std::make_shared<SLE>(keylet::vault(accountID_, sequence));
if (auto ter = dirLink(view(), account_, vault))
if (auto ter = dirLink(view(), accountID_, vault))
return ter;
// We will create Vault and PseudoAccount, hence increase OwnerCount by 2
adjustOwnerCount(view(), owner, 2, j_);
@@ -204,7 +204,7 @@ VaultCreate::doApply()
vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset});
vault->at(sfFlags) = tx.getFlags() & tfVaultPrivate;
vault->at(sfSequence) = sequence;
vault->at(sfOwner) = account_;
vault->at(sfOwner) = accountID_;
vault->at(sfAccount) = pseudoId;
vault->at(sfAssetsTotal) = Number(0);
vault->at(sfAssetsAvailable) = Number(0);
@@ -230,7 +230,7 @@ VaultCreate::doApply()
// Explicitly create MPToken for the vault owner
if (auto const err =
authorizeMPToken(view(), preFeeBalance_, mptIssuanceID, account_, ctx_.journal);
authorizeMPToken(view(), preFeeBalance_, mptIssuanceID, accountID_, ctx_.journal);
!isTesSuccess(err))
return err;
@@ -238,7 +238,7 @@ VaultCreate::doApply()
if (tx.isFlag(tfVaultPrivate))
{
if (auto const err = authorizeMPToken(
view(), preFeeBalance_, mptIssuanceID, pseudoId, ctx_.journal, {}, account_);
view(), preFeeBalance_, mptIssuanceID, pseudoId, ctx_.journal, {}, accountID_);
!isTesSuccess(err))
return err;
}

View File

@@ -122,16 +122,16 @@ VaultDelete::doApply()
}
// Try to remove MPToken for vault shares for the vault owner if it exists.
if (auto const mptoken = view().peek(keylet::mptoken(shareMPTID, account_)))
if (auto const mptoken = view().peek(keylet::mptoken(shareMPTID, accountID_)))
{
if (auto const ter = removeEmptyHolding(view(), account_, MPTIssue(shareMPTID), j_);
if (auto const ter = removeEmptyHolding(view(), accountID_, MPTIssue(shareMPTID), j_);
!isTesSuccess(ter))
{
// LCOV_EXCL_START
JLOG(j_.error()) //
<< "VaultDelete: failed to remove vault owner's MPToken"
<< " MPTID=" << to_string(shareMPTID) //
<< " account=" << toBase58(account_) //
<< " MPTID=" << to_string(shareMPTID) //
<< " account=" << toBase58(accountID_) //
<< " with result: " << transToken(ter);
return ter;
// LCOV_EXCL_STOP

View File

@@ -156,20 +156,20 @@ VaultDeposit::doApply()
auto const& vaultAccount = vault->at(sfAccount);
// Note, vault owner is always authorized
if (vault->isFlag(lsfVaultPrivate) && account_ != vault->at(sfOwner))
if (vault->isFlag(lsfVaultPrivate) && accountID_ != vault->at(sfOwner))
{
if (auto const err = enforceMPTokenAuthorization(
ctx_.view(), mptIssuanceID, account_, preFeeBalance_, j_);
ctx_.view(), mptIssuanceID, accountID_, preFeeBalance_, j_);
!isTesSuccess(err))
return err;
}
else // !vault->isFlag(lsfVaultPrivate) || account_ == vault->at(sfOwner)
else // !vault->isFlag(lsfVaultPrivate) || accountID_ == vault->at(sfOwner)
{
// No authorization needed, but must ensure there is MPToken
if (!view().exists(keylet::mptoken(mptIssuanceID, account_)))
if (!view().exists(keylet::mptoken(mptIssuanceID, accountID_)))
{
if (auto const err = authorizeMPToken(
view(), preFeeBalance_, mptIssuanceID->value(), account_, ctx_.journal);
view(), preFeeBalance_, mptIssuanceID->value(), accountID_, ctx_.journal);
!isTesSuccess(err))
return err;
}
@@ -179,15 +179,15 @@ VaultDeposit::doApply()
{
// This follows from the reverse of the outer enclosing if condition
XRPL_ASSERT(
account_ == vault->at(sfOwner), "xrpl::VaultDeposit::doApply : account is owner");
accountID_ == vault->at(sfOwner), "xrpl::VaultDeposit::doApply : account is owner");
if (auto const err = authorizeMPToken(
view(),
preFeeBalance_, // priorBalance
mptIssuanceID->value(), // mptIssuanceID
sleIssuance->at(sfIssuer), // account
ctx_.journal,
{}, // flags
account_ // holderID
{}, // flags
accountID_ // holderID
);
!isTesSuccess(err))
return err;
@@ -247,15 +247,15 @@ VaultDeposit::doApply()
return tecLIMIT_EXCEEDED;
// Transfer assets from depositor to vault.
if (auto const ter =
accountSend(view(), account_, vaultAccount, assetsDeposited, j_, WaiveTransferFee::Yes);
if (auto const ter = accountSend(
view(), accountID_, vaultAccount, assetsDeposited, j_, WaiveTransferFee::Yes);
!isTesSuccess(ter))
return ter;
// Sanity check
if (accountHolds(
view(),
account_,
accountID_,
assetsDeposited.asset(),
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
@@ -269,7 +269,7 @@ VaultDeposit::doApply()
// Transfer shares from vault to depositor.
if (auto const ter =
accountSend(view(), vaultAccount, account_, sharesCreated, j_, WaiveTransferFee::Yes);
accountSend(view(), vaultAccount, accountID_, sharesCreated, j_, WaiveTransferFee::Yes);
!isTesSuccess(ter))
return ter;

View File

@@ -222,7 +222,7 @@ VaultWithdraw::doApply()
}
if (accountHolds(
view(), account_, share, FreezeHandling::ZeroIfFrozen, AuthHandling::IgnoreAuth, j_) <
view(), accountID_, share, FreezeHandling::ZeroIfFrozen, AuthHandling::IgnoreAuth, j_) <
sharesRedeemed)
{
JLOG(j_.debug()) << "VaultWithdraw: account doesn't hold enough shares";
@@ -251,23 +251,23 @@ VaultWithdraw::doApply()
auto const& vaultAccount = vault->at(sfAccount);
// Transfer shares from depositor to vault.
if (auto const ter =
accountSend(view(), account_, vaultAccount, sharesRedeemed, j_, WaiveTransferFee::Yes);
if (auto const ter = accountSend(
view(), accountID_, vaultAccount, sharesRedeemed, j_, WaiveTransferFee::Yes);
!isTesSuccess(ter))
return ter;
// Try to remove MPToken for shares, if the account balance is zero. Vault
// pseudo-account will never set lsfMPTAuthorized, so we ignore flags.
// Keep MPToken if holder is the vault owner.
if (account_ != vault->at(sfOwner))
if (accountID_ != vault->at(sfOwner))
{
if (auto const ter = removeEmptyHolding(view(), account_, sharesRedeemed.asset(), j_);
if (auto const ter = removeEmptyHolding(view(), accountID_, sharesRedeemed.asset(), j_);
isTesSuccess(ter))
{
JLOG(j_.debug()) //
<< "VaultWithdraw: removed empty MPToken for vault shares"
<< " MPTID=" << to_string(mptIssuanceID) //
<< " account=" << toBase58(account_);
<< " account=" << toBase58(accountID_);
}
else if (ter != tecHAS_OBLIGATIONS)
{
@@ -275,7 +275,7 @@ VaultWithdraw::doApply()
JLOG(j_.error()) //
<< "VaultWithdraw: failed to remove MPToken for vault shares"
<< " MPTID=" << to_string(mptIssuanceID) //
<< " account=" << toBase58(account_) //
<< " account=" << toBase58(accountID_) //
<< " with result: " << transToken(ter);
return ter;
// LCOV_EXCL_STOP
@@ -283,12 +283,12 @@ VaultWithdraw::doApply()
// else quietly ignore, account balance is not zero
}
auto const dstAcct = ctx_.tx[~sfDestination].value_or(account_);
auto const dstAcct = ctx_.tx[~sfDestination].value_or(accountID_);
associateAsset(*vault, vaultAsset);
return doWithdraw(
view(), ctx_.tx, account_, dstAcct, vaultAccount, preFeeBalance_, assetsWithdrawn, j_);
view(), ctx_.tx, accountID_, dstAcct, vaultAccount, preFeeBalance_, assetsWithdrawn, j_);
}
void

View File

@@ -0,0 +1,52 @@
#include <xrpl/tx/wasm/HostFuncImpl.h>
#include <xrpl/basics/Expected.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <cstdint>
namespace xrpl {
// =========================================================
// SECTION: WRITE FUNCTION
// =========================================================
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::updateData(Slice const& data)
{
if (data.size() > kMaxWasmDataLength)
return Unexpected(HostFunctionError::DataFieldTooLarge);
data_ = Bytes(data.begin(), data.end());
return data_->size();
}
// =========================================================
// SECTION: UTILS
// =========================================================
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::checkSignature(
Slice const& message,
Slice const& signature,
Slice const& pubkey) const
{
if (!publicKeyType(pubkey))
return Unexpected(HostFunctionError::InvalidParams);
PublicKey const pk(pubkey);
return verify(pk, message, signature);
}
Expected<Hash, HostFunctionError>
WasmHostFunctionsImpl::computeSha512HalfHash(Slice const& data) const
{
auto const hash = sha512Half(data);
return hash;
}
} // namespace xrpl

View File

@@ -0,0 +1,594 @@
#include <xrpl/basics/Expected.h>
#include <xrpl/basics/Number.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STNumber.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/tx/wasm/HostFunc.h>
#include <xrpl/tx/wasm/HostFuncImpl.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <boost/algorithm/hex.hpp>
#include <cstdint>
#include <iterator>
#include <string>
#include <utility>
#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
#endif
namespace xrpl {
namespace wasm_float {
namespace detail {
class WasmNumber : public Number
{
protected:
static unsigned constexpr encodedFloatSize = 12;
bool good_ = false;
public:
WasmNumber(Slice const& data)
{
if (data.size() != encodedFloatSize)
return;
try
{
SerialIter it(data);
Number const x = STNumber(it, sfNumber).value();
*static_cast<Number*>(this) = x;
}
catch (...)
{
return;
}
good_ = true;
}
WasmNumber(Number const& n) : WasmNumber(n.mantissa(), n.exponent()) // ensure Number canonized
{
}
template <class T>
WasmNumber(T mantissa = 0, int32_t exponent = 0)
{
try
{
Number n;
if constexpr (std::is_signed_v<T>)
{
n = Number(static_cast<int64_t>(mantissa), exponent);
}
else
{
n = Number(static_cast<uint64_t>(mantissa), exponent, Number::Normalized{});
}
*static_cast<Number*>(this) = n;
}
catch (...)
{
return;
}
good_ = true;
}
WasmNumber&
operator=(WasmNumber const&) = default;
explicit
operator bool() const
{
return good_;
}
explicit
operator int64_t() const
{
return Number::operator int64_t();
}
Expected<Bytes, HostFunctionError>
toBytes() const
{
Serializer msg;
STNumber(sfNumber, *this).add(msg);
auto data = msg.getData();
#ifdef DEBUG_OUTPUT
std::cout << "m: " << std::setw(20) << mantissa() << ", e: " << std::setw(12) << exponent()
<< ", hex: ";
std::cout << std::hex << std::uppercase << std::setfill('0');
for (auto const& c : data)
std::cout << std::setw(2) << (unsigned)c << " ";
std::cout << std::dec << std::setfill(' ') << std::endl;
#endif
return Expected<Bytes, HostFunctionError>(std::move(data));
}
};
struct FloatState
{
Number::RoundingMode oldMode;
bool good = false;
FloatState(int32_t mode) : oldMode(Number::getround())
{
if (mode < static_cast<int32_t>(Number::RoundingMode::ToNearest) ||
mode > static_cast<int32_t>(Number::RoundingMode::Upward))
return;
Number::setround(static_cast<Number::RoundingMode>(mode));
good = true;
}
~FloatState()
{
Number::setround(oldMode);
}
operator bool() const
{
return good;
}
};
} // namespace detail
std::string
floatToString(Slice const& data)
{
// set default mode as we don't expect it will be used here
detail::FloatState const rm(static_cast<int32_t>(Number::RoundingMode::ToNearest));
detail::WasmNumber const num(data);
if (!num)
{
std::string hex;
hex.reserve(data.size() * 2);
boost::algorithm::hex(data.begin(), data.end(), std::back_inserter(hex));
return "Invalid data: " + hex;
}
auto const s = to_string(num);
return s;
}
Expected<Bytes, HostFunctionError>
floatFromIntImpl(int64_t x, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
auto const r = num.toBytes();
return r;
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatFromUintImpl(uint64_t x, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
auto const r = num.toBytes();
return r;
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatFromSTAmountImpl(STAmount const& x, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(static_cast<Number>(x));
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
auto const r = num.toBytes();
return r;
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatFromSTNumberImpl(STNumber const& x, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x.value());
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
auto const r = num.toBytes();
return r;
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<int64_t, HostFunctionError>
floatToIntImpl(Slice const& x, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
int64_t const r(num);
return r;
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<FloatPair, HostFunctionError>
floatToMantExpImpl(Slice const& x)
{
try
{
detail::FloatState const rm(static_cast<int32_t>(Number::RoundingMode::ToNearest));
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
return FloatPair(num.mantissa(), num.exponent());
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatFromMantExpImpl(int64_t mantissa, int32_t exponent, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(mantissa, exponent);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed);
return num.toBytes();
}
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
}
Expected<int32_t, HostFunctionError>
floatCompareImpl(Slice const& x, Slice const& y)
{
try
{
// set default mode as we don't expect it will be used here
detail::FloatState const rm(static_cast<int32_t>(Number::RoundingMode::ToNearest));
detail::WasmNumber const xx(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
if (xx < yy)
return 2;
if (xx == yy)
return 0;
return 1;
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatAddImpl(Slice const& x, Slice const& y, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res = xx + yy;
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res = xx - yy;
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res = xx * yy;
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatDivideImpl(Slice const& x, Slice const& y, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res = xx / yy;
return res.toBytes();
}
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
}
Expected<Bytes, HostFunctionError>
floatRootImpl(Slice const& x, int32_t n, int32_t mode)
{
try
{
if (n < 1)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res(root(xx, n));
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatPowerImpl(Slice const& x, int32_t n, int32_t mode)
{
try
{
if ((n < 0) || (n > Number::kMaxExponent))
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
if (xx == Number() && (n == 0))
return Unexpected(HostFunctionError::InvalidParams);
detail::WasmNumber const res(power(xx, n, 1));
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FloatComputationError);
}
// LCOV_EXCL_STOP
}
} // namespace wasm_float
// =========================================================
// ACTUAL HOST FUNCTIONS
// =========================================================
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatFromInt(int64_t x, int32_t mode) const
{
return wasm_float::floatFromIntImpl(x, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatFromUint(uint64_t x, int32_t mode) const
{
return wasm_float::floatFromUintImpl(x, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatFromSTAmount(STAmount const& x, int32_t mode) const
{
return wasm_float::floatFromSTAmountImpl(x, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatFromSTNumber(STNumber const& x, int32_t mode) const
{
return wasm_float::floatFromSTNumberImpl(x, mode);
}
Expected<int64_t, HostFunctionError>
WasmHostFunctionsImpl::floatToInt(Slice const& x, int32_t mode) const
{
return wasm_float::floatToIntImpl(x, mode);
}
Expected<FloatPair, HostFunctionError>
WasmHostFunctionsImpl::floatToMantExp(Slice const& x) const
{
return wasm_float::floatToMantExpImpl(x);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const
{
return wasm_float::floatFromMantExpImpl(mantissa, exponent, mode);
}
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::floatCompare(Slice const& x, Slice const& y) const
{
return wasm_float::floatCompareImpl(x, y);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatAdd(Slice const& x, Slice const& y, int32_t mode) const
{
return wasm_float::floatAddImpl(x, y, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatSubtract(Slice const& x, Slice const& y, int32_t mode) const
{
return wasm_float::floatSubtractImpl(x, y, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatMultiply(Slice const& x, Slice const& y, int32_t mode) const
{
return wasm_float::floatMultiplyImpl(x, y, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatDivide(Slice const& x, Slice const& y, int32_t mode) const
{
return wasm_float::floatDivideImpl(x, y, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatRoot(Slice const& x, int32_t n, int32_t mode) const
{
return wasm_float::floatRootImpl(x, n, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatPower(Slice const& x, int32_t n, int32_t mode) const
{
return wasm_float::floatPowerImpl(x, n, mode);
}
} // namespace xrpl

View File

@@ -0,0 +1,395 @@
#include <xrpl/basics/Expected.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/STBitString.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/tx/wasm/HostFuncImpl.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <cstdint>
#include <utility>
#include <variant>
namespace xrpl {
using FieldValue = std::variant<STBase const*, uint256 const*>;
template <class T>
Bytes
getIntBytes(STBase const* obj)
{
static_assert(std::is_integral_v<T>, "Only integral types");
XRPL_ASSERT(obj, "getIntBytes null pointer");
auto const* num(static_cast<STInteger<T> const*>(obj)); // NOLINT
T const data = adjustWasmEndianess(num->value());
uint8_t const* b = reinterpret_cast<uint8_t const*>(&data);
return Bytes{b, b + sizeof(T)};
}
static Expected<Bytes, HostFunctionError>
getAnyFieldData(STBase const* obj)
{
if (obj == nullptr)
return Unexpected(HostFunctionError::FieldNotFound);
auto const stype = obj->getSType();
switch (stype)
{
// LCOV_EXCL_START
case STI_UNKNOWN:
case STI_NOTPRESENT:
return Unexpected(HostFunctionError::FieldNotFound);
// LCOV_EXCL_STOP
case STI_OBJECT:
case STI_ARRAY:
case STI_VECTOR256:
return Unexpected(HostFunctionError::NotLeafField);
case STI_ACCOUNT: {
auto const* account(static_cast<STAccount const*>(obj)); // NOLINT
auto const& data = account->value();
return Bytes{data.begin(), data.end()};
}
case STI_ISSUE: {
auto const* issue(static_cast<STIssue const*>(obj)); // NOLINT
Asset const& asset(issue->value());
// XRP and IOU will be processed by serializer
if (asset.holds<MPTIssue>())
{
auto const& mptIssue = asset.get<MPTIssue>();
auto const& mptID = mptIssue.getMptID();
return Bytes{mptID.cbegin(), mptID.cend()};
}
break; // Use serializer
}
case STI_VL: {
auto const* vl(static_cast<STBlob const*>(obj)); // NOLINT
auto const& data = vl->value();
return Bytes{data.begin(), data.end()};
}
case STI_UINT16:
return getIntBytes<std::uint16_t>(obj);
case STI_UINT32:
return getIntBytes<std::uint32_t>(obj);
// LCOV_EXCL_START
case STI_UINT64:
return getIntBytes<std::uint64_t>(obj);
case STI_INT32:
return getIntBytes<std::int32_t>(obj);
case STI_INT64:
return getIntBytes<std::int64_t>(obj);
// LCOV_EXCL_STOP
case STI_UINT256: {
auto const* uint256Obj(static_cast<STUInt256 const*>(obj)); // NOLINT
auto const& data = uint256Obj->value();
return Bytes{data.begin(), data.end()};
}
case STI_AMOUNT:
case STI_NUMBER:
default:
break; // Use serializer
}
Serializer msg;
obj->add(msg);
return msg.getData();
}
static Expected<Bytes, HostFunctionError>
getAnyFieldData(FieldValue const& variantObj)
{
if (STBase const* const* obj = std::get_if<STBase const*>(&variantObj))
return getAnyFieldData(*obj);
if (uint256 const* const* u = std::get_if<uint256 const*>(&variantObj))
return Bytes((*u)->begin(), (*u)->end());
return Unexpected(HostFunctionError::Internal); // LCOV_EXCL_LINE
}
static inline bool
noField(STBase const* field)
{
return (field == nullptr) || (STI_NOTPRESENT == field->getSType()) ||
(STI_UNKNOWN == field->getSType());
}
static Expected<FieldValue, HostFunctionError>
locateField(STObject const& obj, FieldLocator const& locator)
{
STBase const* field = nullptr;
auto const& knownSFields = SField::getKnownCodeToField();
{
int32_t const sfieldCode = adjustWasmEndianess(locator[0]);
auto const it = knownSFields.find(sfieldCode);
if (it == knownSFields.end())
return Unexpected(HostFunctionError::InvalidField);
auto const& fname(*it->second);
field = obj.peekAtPField(fname);
if (noField(field))
return Unexpected(HostFunctionError::FieldNotFound);
}
for (unsigned i = 1; i < locator.size(); ++i)
{
int32_t const sfieldCode = adjustWasmEndianess(locator[i]);
if (STI_ARRAY == field->getSType())
{
auto const* arr = static_cast<STArray const*>(field); // NOLINT
if (sfieldCode < 0 || std::cmp_greater_equal(sfieldCode, arr->size()))
return Unexpected(HostFunctionError::IndexOutOfBounds);
field = &(arr->operator[](sfieldCode));
}
else if (STI_OBJECT == field->getSType())
{
auto const* o = static_cast<STObject const*>(field); // NOLINT
auto const it = knownSFields.find(sfieldCode);
if (it == knownSFields.end())
return Unexpected(HostFunctionError::InvalidField);
auto const& fname(*it->second);
field = o->peekAtPField(fname);
}
else if (STI_VECTOR256 == field->getSType())
{
auto const* v = static_cast<STVector256 const*>(field); // NOLINT
if (sfieldCode < 0 || std::cmp_greater_equal(sfieldCode, v->size()))
return Unexpected(HostFunctionError::IndexOutOfBounds);
return FieldValue(&(v->operator[](sfieldCode)));
}
else // simple field must be the last one
{
return Unexpected(HostFunctionError::LocatorMalformed);
}
if (noField(field))
return Unexpected(HostFunctionError::FieldNotFound);
}
return FieldValue(field);
}
static inline Expected<int32_t, HostFunctionError>
getArrayLen(FieldValue const& variantField)
{
if (STBase const* const* field = std::get_if<STBase const*>(&variantField))
{
if ((*field)->getSType() == STI_VECTOR256)
return static_cast<STVector256 const*>(*field)->size(); // NOLINT
if ((*field)->getSType() == STI_ARRAY)
return static_cast<STArray const*>(*field)->size(); // NOLINT
}
// uint256 is not an array so that variant should still return NO_ARRAY
return Unexpected(HostFunctionError::NoArray); // LCOV_EXCL_LINE
}
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
{
auto const& keylet = keylet::unchecked(objId);
if (cacheIdx < 0 || cacheIdx > maxCache)
return Unexpected(HostFunctionError::SlotOutRange);
if (cacheIdx == 0)
{
for (cacheIdx = 0; cacheIdx < maxCache; ++cacheIdx)
{
if (!cache_[cacheIdx])
break;
}
}
else
{
cacheIdx--; // convert to 0-based index
}
if (cacheIdx >= maxCache)
return Unexpected(HostFunctionError::SlotsFull);
cache_[cacheIdx] = ctx_.view().read(keylet);
if (!cache_[cacheIdx])
return Unexpected(HostFunctionError::LedgerObjNotFound);
return cacheIdx + 1; // return 1-based index
}
// Subsection: top level getters
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::getTxField(SField const& fname) const
{
return getAnyFieldData(ctx_.tx.peekAtPField(fname));
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname) const
{
auto const sle = getCurrentLedgerObj();
if (!sle.has_value())
return Unexpected(sle.error());
return getAnyFieldData(sle.value()->peekAtPField(fname));
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::getLedgerObjField(int32_t cacheIdx, SField const& fname) const
{
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
if (!normalizedIdx.has_value())
return Unexpected(normalizedIdx.error());
return getAnyFieldData(cache_[normalizedIdx.value()]->peekAtPField(fname));
}
// Subsection: nested getters
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::getTxNestedField(FieldLocator const& locator) const
{
auto const r = locateField(ctx_.tx, locator);
if (!r)
return Unexpected(r.error());
return getAnyFieldData(r.value());
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(FieldLocator const& locator) const
{
auto const sle = getCurrentLedgerObj();
if (!sle.has_value())
return Unexpected(sle.error());
auto const r = locateField(*sle.value(), locator);
if (!r)
return Unexpected(r.error());
return getAnyFieldData(r.value());
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::getLedgerObjNestedField(int32_t cacheIdx, FieldLocator const& locator) const
{
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
if (!normalizedIdx.has_value())
return Unexpected(normalizedIdx.error());
auto const r = locateField(*cache_[normalizedIdx.value()], locator);
if (!r)
return Unexpected(r.error());
return getAnyFieldData(r.value());
}
// Subsection: array length getters
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::getTxArrayLen(SField const& fname) const
{
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
return Unexpected(HostFunctionError::NoArray);
auto const* field = ctx_.tx.peekAtPField(fname);
if (noField(field))
return Unexpected(HostFunctionError::FieldNotFound);
return getArrayLen(field);
}
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname) const
{
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
return Unexpected(HostFunctionError::NoArray);
auto const sle = getCurrentLedgerObj();
if (!sle.has_value())
return Unexpected(sle.error());
auto const* field = sle.value()->peekAtPField(fname);
if (noField(field))
return Unexpected(HostFunctionError::FieldNotFound);
return getArrayLen(field);
}
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const
{
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
return Unexpected(HostFunctionError::NoArray);
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
if (!normalizedIdx.has_value())
return Unexpected(normalizedIdx.error());
auto const* field = cache_[normalizedIdx.value()]->peekAtPField(fname);
if (noField(field))
return Unexpected(HostFunctionError::FieldNotFound);
return getArrayLen(field);
}
// Subsection: nested array length getters
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::getTxNestedArrayLen(FieldLocator const& locator) const
{
auto const r = locateField(ctx_.tx, locator);
if (!r)
return Unexpected(r.error());
auto const& field = r.value();
return getArrayLen(field);
}
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(FieldLocator const& locator) const
{
auto const sle = getCurrentLedgerObj();
if (!sle.has_value())
return Unexpected(sle.error());
auto const r = locateField(*sle.value(), locator);
if (!r)
return Unexpected(r.error());
auto const& field = r.value();
return getArrayLen(field);
}
Expected<int32_t, HostFunctionError>
WasmHostFunctionsImpl::getLedgerObjNestedArrayLen(int32_t cacheIdx, FieldLocator const& locator)
const
{
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
if (!normalizedIdx.has_value())
return Unexpected(normalizedIdx.error());
auto const r = locateField(*cache_[normalizedIdx.value()], locator);
if (!r)
return Unexpected(r.error());
auto const& field = r.value();
return getArrayLen(field);
}
} // namespace xrpl

Some files were not shown because too many files have changed in this diff Show More