Compare commits

...

191 Commits

Author SHA1 Message Date
Peng Wang
e3c8dbb46f Merge branch 'ripple/smart-escrow' into ripple/se/supported
Conflict resolution:

- features.macro: kept fixCleanup3_4_0 from develop and kept SmartEscrow at
  Supported::Yes, which is the only thing this branch changes.
2026-08-09 19:49:19 -04:00
Peng Wang
5ae09518b8 Merge branch 'ripple/se/fees' into ripple/smart-escrow
Conflict resolution:

- sfields.macro: both sides claimed INT32 code 2. sfRemainingOwnerCountDelta
  is already in develop, so it keeps code 2 and sfVMReturnCode moves to 3.
  This changes the Smart Escrow serialization, which is safe while the
  amendment is unactivated, but is a deliberate renumber rather than a
  mechanical merge.
- EscrowFinish.cpp: took the SeqProxy form of the keylet::escrow call and
  kept this branch's comment.

Follow-on fixes the merge required:

- Adapted two keylet::escrow calls in EscrowSmart_test.cpp to SeqProxy; they
  are only on this branch, so the earlier pass over the wasm code did not
  cover them.
- Updated two gas expectations that the trace host function refactor and the
  regenerated fixtures invalidated: the codecov allowance in Wasm_test.cpp is
  now 264'467 and sfGasUsed in EscrowSmart_test.cpp is now 48'433. Both are
  measured, not estimated.
- Disabled testKeyletHostFunctions. kAllKeyletsWasmHex was built against the
  old trace ABI and is rejected with temINVALID_BYTECODE. Regenerating it
  needs the all_keylets source ported to the current stdlib first, along with
  float_tests and float_0, which are stale for the same reason. See the TODO
  on the test.
2026-08-09 18:17:53 -04:00
pwang200
ef438aec26 finish fn result refactor (#7947) 2026-08-08 23:13:33 -04:00
Mayukha Vadari
5fd5839f2b Merge branch 'ripple/smart-escrow' into ripple/se/supported 2026-08-05 16:44:54 -04:00
Mayukha Vadari
75fac65e27 change definitions config 2026-07-27 12:21:08 -04:00
Mayukha Vadari
ecae6b6346 Merge pull request #7866 from XRPLF/mvadari/smart-escrow-merge-july
chore: Update `ripple/smart-escrow` branch from base
2026-07-27 09:12:19 -04:00
Mayukha Vadari
6f4fe178dd fix tests 2026-07-24 19:29:26 -04:00
Mayukha Vadari
083986ab82 fix clang-tidy issues 2026-07-24 14:16:36 -04:00
Mayukha Vadari
c145201538 fix tests 2026-07-24 14:15:39 -04:00
Mayukha Vadari
4fbbd6ff59 fix build 2026-07-24 12:03:43 -04:00
Mayukha Vadari
39c95fee3b Merge branch 'ripple/se/fees' of https://github.com/XRPLF/rippled into ripple/smart-escrow 2026-07-24 11:23:01 -04:00
Rithvik Reddygari
a41889df31 refactor: SField and TER code renames (#7797) 2026-07-20 12:29:39 -04:00
Mayukha Vadari
62d9f40d33 Mark SmartEscrow as Supported::yes 2026-05-27 14:17:39 -04:00
Mayukha Vadari
d3550fac60 Merge pull request #7303 from XRPLF/mvadari/se-escrow-update
chore: Update branch from develop
2026-05-27 14:12:19 -04:00
Mayukha Vadari
10ef15fff1 fix clang-tidy 2026-05-27 10:08:52 -04:00
Mayukha Vadari
2307cfdc5a fix clang-tidy 2026-05-26 20:09:46 -04:00
Mayukha Vadari
3b328cfc45 fix broken builds 2026-05-26 18:53:18 -04:00
Mayukha Vadari
e20b2430ba update gas amounts 2026-05-26 18:29:28 -04:00
Mayukha Vadari
99072fbddf one more 2026-05-26 17:59:03 -04:00
Mayukha Vadari
059cefa35a fix typos 2026-05-26 17:58:38 -04:00
Mayukha Vadari
a2205190a6 fix build 2026-05-26 17:57:28 -04:00
Mayukha Vadari
4c657bc92e fix build 2026-05-20 17:06:43 -04:00
Mayukha Vadari
ac481a0bd7 fix clang-tidy 2026-05-20 16:47:52 -04:00
Mayukha Vadari
7eacd9d735 Merge remote-tracking branch 'upstream/ripple/se/fees' into mvadari/se-escrow-update 2026-05-20 16:43:57 -04:00
Mayukha Vadari
d562129e7c Merge branch 'ripple/se/fees' into ripple/smart-escrow 2026-04-13 17:01:46 -04:00
Mayukha Vadari
74beb5f80d Merge branch 'ripple/se/fees' of https://github.com/XRPLF/rippled into ripple/smart-escrow 2026-04-08 13:52:24 -04:00
Mayukha Vadari
4bae4d781f fix build 2026-04-08 11:43:21 -04:00
Mayukha Vadari
01a5e07830 Merge branch 'ripple/se/fees' of https://github.com/XRPLF/rippled into ripple/smart-escrow 2026-04-08 09:58:40 -04:00
Mayukha Vadari
fe2e5ce649 fix clang-tidy issues 2026-04-03 12:33:14 -04:00
Mayukha Vadari
6afa51142d Merge branch 'ripple/se/fees' of https://github.com/XRPLF/rippled into ripple/smart-escrow 2026-04-03 11:06:16 -04:00
Mayukha Vadari
0acfa1c4f2 fix merge issues 2026-04-02 19:22:52 -04:00
Mayukha Vadari
309fdfc3f2 Merge branch 'ripple/se/fees' of https://github.com/XRPLF/rippled into ripple/smart-escrow 2026-04-02 19:08:08 -04:00
Mayukha Vadari
f8a149c675 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2026-03-24 09:10:37 -07:00
Olek
780380da7e Modularization fixes (#6488) 2026-03-10 11:37:12 -04:00
Mayukha Vadari
fdfdf4fceb fix pre-commit 2026-03-05 14:49:07 -04:00
Mayukha Vadari
a76bd834b6 Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow [WIP, broken] 2026-03-05 14:45:30 -04:00
Mayukha Vadari
50a74b899d add defensive check for sfData length (#6449) 2026-03-02 14:47:47 -05:00
Mayukha Vadari
446ad36cbb Merge branch 'ripple/se/fees' into ripple/smart-escrow 2026-02-10 17:47:32 -05:00
Mayukha Vadari
fd14054f17 update preflight checks (#6094) 2026-02-09 17:36:02 -05:00
Mayukha Vadari
4318b2ebf7 fix build issue 2026-02-06 12:44:05 -05:00
Mayukha Vadari
e6ee492822 try again 2026-02-05 16:27:44 -05:00
Mayukha Vadari
d4510147d1 fix build issue 2026-02-05 14:42:14 -05:00
Mayukha Vadari
a9a94fbf1a fix tests 2026-02-05 13:24:50 -05:00
Mayukha Vadari
719ba392db Merge branch 'ripple/smart-escrow' into ripple/se/fees 2026-02-04 18:41:29 -05:00
Mayukha Vadari
f01ac563a9 Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow 2026-02-04 18:07:12 -05:00
Mayukha Vadari
a1844086d7 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2026-02-03 15:32:21 -05:00
Mayukha Vadari
ac173b6827 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2026-01-28 17:14:13 -05:00
Mayukha Vadari
430696682d Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow 2026-01-26 09:43:14 -05:00
Mayukha Vadari
57d2a91ad5 test large WASM modules (#6206)
* [WIP] first attempt at large wasm test

* finish large WASM modules

* fix windows build (hopefully)

* Apply suggestions from code review

* respond to comments

* add file and line to fail

* clean up test

* add source_location

* simplify

* fix windows
2026-01-23 14:45:09 -05:00
Olek
94b35a234e Make hostfunctions object shared (#6252) 2026-01-21 15:27:06 -05:00
Mayukha Vadari
b5d0078927 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2026-01-21 13:26:37 -05:00
Mayukha Vadari
9538e9b34c Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow 2026-01-15 14:34:54 -05:00
pwang200
fb97f7b596 fix unit tests failed due to fuel changes (#6174)
* fix unit tests failed due to fuel changes

* fix tests

* fix tests

---------

Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
2026-01-13 16:48:25 -05:00
Mayukha Vadari
845c503ea6 Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow 2026-01-13 13:48:15 -05:00
Mayukha Vadari
ff39fa59d9 Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow 2026-01-12 14:12:04 -05:00
Mayukha Vadari
8015088340 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2026-01-08 11:46:18 -05:00
Mayukha Vadari
61b2fe4f64 fix test 2026-01-06 17:58:22 -05:00
Mayukha Vadari
7ee964f514 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2026-01-06 17:22:42 -05:00
Mayukha Vadari
6ffbef09c2 fix gas in test 2025-12-23 11:00:57 -08:00
Mayukha Vadari
e05f907788 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-12-22 17:02:12 -08:00
Mayukha Vadari
e916416642 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-12-15 10:47:26 -08:00
Mayukha Vadari
6a54ed7f14 fix fee overflow issue in EscrowFinish (#6130) 2025-12-12 15:12:43 -08:00
Olek
1e0741690d Fix sign cost (#6103) 2025-12-03 18:27:06 -05:00
Olek
c5d178f152 HF cost for smart escrow (#6097) 2025-12-02 13:12:15 -05:00
Mayukha Vadari
5a17940e2a Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-12-02 07:48:22 -05:00
Mayukha Vadari
abfcc4ef67 fix tests 2025-11-25 04:17:42 +05:30
Mayukha Vadari
dba187f8c5 fix build issues 2025-11-25 03:42:38 +05:30
Mayukha Vadari
8f2f8d53b4 update gas amounts for wasmi 2025-11-25 03:32:01 +05:30
Mayukha Vadari
49acc61961 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-11-25 03:31:10 +05:30
Mayukha Vadari
def7758a23 remove copyright stuff 2025-11-04 17:53:13 -05:00
Mayukha Vadari
58e5b4ad25 Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow 2025-11-04 17:52:17 -05:00
Mayukha Vadari
fa8aa49376 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-11-04 14:51:11 -05:00
Mayukha Vadari
a891b49c67 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-11-04 14:24:51 -05:00
Mayukha Vadari
7e2e10f02c Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-24 18:01:33 -04:00
Mayukha Vadari
22ca691e75 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-24 17:14:54 -04:00
Mayukha Vadari
fc6ff69752 fix tests 2025-10-24 17:14:46 -04:00
Mayukha Vadari
079e251aca fix bug 2025-10-24 16:52:17 -04:00
Mayukha Vadari
b6bd268be2 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-24 16:02:23 -04:00
Mayukha Vadari
566b85b3d6 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-23 15:41:53 -04:00
Mayukha Vadari
7d22fe804d Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-23 15:38:46 -04:00
Mayukha Vadari
9cfb7ac340 fix build issue 2025-10-20 17:20:35 -04:00
Mayukha Vadari
3a0e9aab4f Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-20 14:51:15 -04:00
Mayukha Vadari
3b6cd22e32 fix 2025-10-14 18:10:11 -04:00
Mayukha Vadari
c9c35780d2 reduce diff more 2025-10-14 18:00:58 -04:00
Mayukha Vadari
17f401f374 reduce diff 2025-10-14 17:58:41 -04:00
Mayukha Vadari
c6c54b3282 resolve todos 2025-10-14 17:56:12 -04:00
Mayukha Vadari
91455b6860 respond to comments 2025-10-13 15:20:03 -04:00
Mayukha Vadari
f16f243c22 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-13 14:01:11 -04:00
Mayukha Vadari
e41f6a71b7 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-09 17:13:13 -04:00
Mayukha Vadari
db263b696c Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-10-06 17:01:32 -04:00
Mayukha Vadari
c41e52f57a Move Smart Escrow tests to separate file (#5849) 2025-10-06 16:27:21 -04:00
Mayukha Vadari
55772a0d07 add sfData preflight checks + tests (#5839) 2025-10-02 17:50:43 -04:00
Mayukha Vadari
965a9e89ac Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow 2025-10-02 15:51:35 -04:00
Mayukha Vadari
8d266d3941 remove STInt64 (#5815) 2025-09-29 15:43:10 -04:00
Mayukha Vadari
a865b4da1c Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-09-26 17:10:10 -04:00
Mayukha Vadari
8729688feb Merge remote-tracking branch 'upstream/ripple/se/fees' into ripple/smart-escrow 2025-09-26 16:56:51 -04:00
Mayukha Vadari
f1f798bb85 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-09-26 15:52:22 -04:00
Mayukha Vadari
85bff20ae5 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-09-23 10:22:30 -04:00
Mayukha Vadari
e6592e93a9 Merge branch 'ripple/se/fees' into ripple/smart-escrow 2025-09-22 18:32:36 -04:00
Mayukha Vadari
2f869b3cfc Merge branch 'develop' into ripple/smart-escrow 2025-09-11 16:41:14 -04:00
Mayukha Vadari
ffa21c27a7 fix test 2025-09-11 16:40:59 -04:00
Mayukha Vadari
5c480cf883 Merge branch 'develop' into ripple/smart-escrow 2025-09-10 14:51:45 -04:00
Mayukha Vadari
adc64e7866 Merge branch 'develop' into ripple/smart-escrow 2025-09-10 10:46:51 -04:00
Mayukha Vadari
4d4a1cfe82 Merge branch 'develop' into ripple/smart-escrow 2025-09-09 16:19:27 -04:00
Mayukha Vadari
f2c7da3705 Merge branch 'develop' into ripple/smart-escrow 2025-09-09 14:51:25 -04:00
Mayukha Vadari
3ab0a82cd3 Merge branch 'develop' into ripple/smart-escrow 2025-09-08 15:20:01 -04:00
Mayukha Vadari
a46d772147 fix build and tests (#5768)
* fix conan.lock

* add conan.lock to triggers

* update on-trigger.yml too

* fix tests

* roll back unrelated changes
2025-09-04 17:05:39 -04:00
Mayukha Vadari
f3c50318e8 Merge branch 'develop' into ripple/smart-escrow 2025-09-04 13:42:59 -04:00
Mayukha Vadari
e7aa924c0e Merge branch 'develop' into ripple/smart-escrow 2025-09-03 15:54:57 -04:00
Mayukha Vadari
5266f04970 chore: rollback unrelated changes (#5737) 2025-09-02 18:26:01 -04:00
Mayukha Vadari
db957cf191 Merge branch 'develop' into ripple/smart-escrow 2025-08-29 16:54:17 -04:00
Mayukha Vadari
8ac514363d get new fees and reserves working (#5714) 2025-08-29 10:58:52 -04:00
Mayukha Vadari
c2ea68cca4 Merge branch 'develop' into ripple/smart-escrow 2025-08-28 14:02:10 -04:00
Mayukha Vadari
3d86881ce7 Merge branch 'develop' into ripple/smart-escrow 2025-08-27 13:58:38 -04:00
Mayukha Vadari
697d1470f4 change: adjust the function signatures for get_ledger_sqn and get_parent_ledger_time (#5733) 2025-08-27 13:58:27 -04:00
Mayukha Vadari
0b5f8f4051 Merge remote-tracking branch 'upstream/develop' into ripple/smart-escrow 2025-08-26 17:59:43 -04:00
Mayukha Vadari
0fed78fbcc Merge branch 'develop' into ripple/smart-escrow 2025-08-26 15:04:33 -04:00
Mayukha Vadari
8c38ef726b chore: exclude a bunch of code that doesn't need to be tested from codecov (#5721)
* exclude the bulk of HostFunc.h from codecov

* fix codecov (maybe)

* adjust

* more codecov excl
2025-08-26 15:04:27 -04:00
Mayukha Vadari
2399d90334 Merge branch 'develop' into ripple/smart-escrow 2025-08-25 10:42:48 -04:00
Mayukha Vadari
6367d68d1e Merge branch 'develop' into ripple/smart-escrow 2025-08-22 14:02:12 -04:00
Mayukha Vadari
155a84c8a3 Merge branch 'develop' into ripple/smart-escrow 2025-08-22 13:24:43 -04:00
Mayukha Vadari
10558c9eff Merge remote-tracking branch 'upstream/develop' into develop5.5 2025-08-22 10:38:10 -04:00
Mayukha Vadari
dd30d811e6 feat: last set of host functions (#5674) 2025-08-15 16:51:30 -04:00
Mayukha Vadari
293d8e4ddb test: store Rust source code in rippled, instead of just opaque hex strings (#5653) 2025-08-15 15:32:36 -04:00
Mayukha Vadari
77875c9133 fix: actually return int instead of bool (#5651) 2025-08-15 13:58:38 -04:00
Olek
647b47567e Fix float point binary format (#5688) 2025-08-15 09:51:40 -04:00
Olek
b0a1ad3b06 Disable float point instructions (#5679) 2025-08-14 14:59:55 -04:00
Mayukha Vadari
1d141bf2e8 chore: move WASM files to separate folder (#5666) 2025-08-14 11:46:10 -04:00
Olek
0d0e279ae2 Float point Hostfunctions unit tests (#5656)
* Added direct unittests for float hostfunctions
2025-08-14 10:21:21 -04:00
Mayukha Vadari
5dc0cee28a cache data instead of setting it in updateData (#5642)
Co-authored-by: Oleksandr <115580134+oleks-rip@users.noreply.github.com>
2025-08-11 15:52:21 -04:00
Mayukha Vadari
c15947da56 fix CI 2025-08-06 12:41:12 -04:00
Mayukha Vadari
9bc04244e7 Merge remote-tracking branch 'upstream/ripple/smart-escrow' into develop5 2025-08-06 12:36:25 -04:00
Mayukha Vadari
38c7a27010 clean up WASM functions a bit (#5628) 2025-08-05 18:06:34 -04:00
Mayukha Vadari
58741d2791 feat: return an int instead of boolean from finish, display in metadata (#5641)
* create STInt32 and STInt64, use it for sfWasmReturnCode in metadata

* get it actually working

* add tests

* update comment

* change type

* respond to comments
2025-08-04 09:25:47 -04:00
Mayukha Vadari
8426470506 feat: add other misc host functions (#5574) 2025-07-31 18:39:21 -04:00
Olek
ccc3280b1a Update wamr to 2.4.1 (#5640) 2025-07-31 13:42:53 -04:00
Mayukha Vadari
2847075705 fix: ensure GasUsed shows up in the metadata even on tecWASM_REJECTED (#5633)
* always set gas used

* fix

* add tests

* clean up
2025-07-31 10:57:56 -04:00
Olek
3108ca0549 Float point HF (#5611)
- added support for 8-byte  float point
2025-07-30 14:38:03 +00:00
Mayukha Vadari
3b849ff497 Add unit tests for host functions (#5578) 2025-07-29 17:54:48 -04:00
Mayukha Vadari
66776b6a85 test: codecov for WasmHostFuncWrapper.cpp (#5601) 2025-07-29 16:06:21 -04:00
Mayukha Vadari
c8c241b50d Merge remote-tracking branch 'upstream/develop' into develop4.6 2025-07-29 11:34:44 -04:00
Bronek Kozicki
44cb588371 Build options cleanup (#5581)
As we no longer support old compiler versions, we are bringing back some warnings by removing no longer relevant `-Wno-...` options.
2025-07-28 13:02:41 -04:00
Oleksandr
6f91b8f8d1 Fix windows 2025-07-28 12:29:39 -04:00
Mayukha Vadari
3d93379132 add header 2025-07-25 15:10:47 -04:00
Mayukha Vadari
84fd7d0126 Merge remote-tracking branch 'upstream/develop' into develop4.6 2025-07-25 14:56:36 -04:00
Mayukha Vadari
7f52287aae rename variables (#5609)
* rename variables

* instanceWrapper -> runtime

* size -> srcSize

* begin -> ptr
2025-07-25 11:54:31 -04:00
Mayukha Vadari
98b8986868 fix merge issues (mostly with Conan2 upgrade) 2025-07-23 15:52:03 -04:00
Mayukha Vadari
250f2842ee Merge remote-tracking branch 'upstream/develop' into develop4.5 2025-07-23 13:43:47 -04:00
Olek
9eca1a3a0c MPT and IOU support for amount and issue (#5573)
* MPT and IOU support for ammount and issue

* Fix tests
Update wasm code to the latest version
Remove deprecated tests
Remove deprecated wasm
2025-07-22 17:43:21 +00:00
Mayukha Vadari
24b7a03224 feat: add more keylet host functions (#5522) 2025-07-17 12:37:41 -04:00
Mayukha Vadari
9007097d24 Simplify host function boilerplate (#5534)
* enum for HF errors

* switch getData functions to be templates

* getData<SField> working

* Slice -> Bytes in host functions

* RET -> helper function instead of macro

* get template function working

* more organization/cleanup

* fix failures

* more cleanup

* Bytes -> Slice

* SFieldParam macro -> type alias

* fix return type

* fix bugs

* replace std::make_index_sequence

* remove `failed` from output

* remove complex function

* more uniformity

* respond to comments

* enum class HostFunctionError

* rename variable

* respond to comments

* remove templating

* [WIP] basic getData tests

* weird linker error

* fix issue
2025-07-15 04:28:59 +05:30
Olek
bc445ec6a2 Add hostfunctions schedule table
Remove opcode schedule table from wamr
2025-07-11 18:08:36 -04:00
Mayukha Vadari
4fa0ae521e disallow a computation allowance of 0 (#5541) 2025-07-09 00:34:17 +05:30
Olek
7bdf5fa8b8 Fix build.md wamr version (#5535) 2025-07-04 00:48:03 +05:30
Olek
65b0b976d9 Sync error codes (#5527)
* Sync error codes
2025-07-02 17:33:39 -04:00
Elliot.
a0d275feec chore: Clear CODEOWNERS (#5528) 2025-07-02 10:39:57 -07:00
Mayukha Vadari
ece3a8d7be Merge branch 'develop' into develop4 2025-06-30 21:33:30 +05:30
Olek
463acf51b5 preflight checks for wasm (#5517) 2025-06-30 09:34:38 -04:00
Olek
1cd16fab87 Host-functions perf test fixes (#5514) 2025-06-27 09:59:28 -04:00
Olek
add55c4f33 Host functions gas cost for wasm_runtime interface (#5500) 2025-06-25 14:04:04 +00:00
Olek
51a9c0ff59 Host function gas cost (#5488)
* Update Wamr to 2.3.1
* Add gas cost per host-function
* Fix windows build
* Fix wasm test
* Add no import test
2025-06-12 15:54:49 -04:00
Mayukha Vadari
6e8a5f0f4e fix: make host function traces easier to use, fix get_NFT bug (#5466)
Co-authored-by: Olek <115580134+oleks-rip@users.noreply.github.com>
2025-06-05 14:24:13 -04:00
Mayukha Vadari
8a33702f26 fix merge issues 2025-06-05 12:38:26 -04:00
Mayukha Vadari
a072d49802 Merge remote-tracking branch 'upstream/ripple/smart-escrow' into develop3.5 2025-06-05 11:51:53 -04:00
Mayukha Vadari
a0aeeb8e07 Merge remote-tracking branch 'upstream/develop' into develop3.5 2025-06-05 11:50:38 -04:00
Olek
383b225690 Fix processing nonexistent field (#5467) 2025-06-04 17:32:11 -04:00
Mayukha Vadari
ace2247800 Merge remote-tracking branch 'upstream/ripple/smart-escrow' into develop3.5 2025-06-04 14:15:17 -04:00
Olek
6a6fed5dce More hostfunctions (#5451)
* Bug fixes:
- Fix bugs found during schedule table tests
- Add more tests
- Add parameters passing for runEscrowWasm function

* Add new host-functions
 fix wamr logging
 add runtime passing through HF
 fix runEscrowWasm interface

* Improve logs

* Fix logging bug

* Set 4k limit for update_data HF

* allHF wasm module fixes
2025-05-30 19:01:27 -04:00
Mayukha Vadari
1f8aece8cd feat: add a GasUsed parameter to the metadata (#5456) 2025-05-29 16:36:55 -04:00
Mayukha Vadari
6c6f8cd4f9 Merge remote-tracking branch 'upstream/develop' into develop3 2025-05-29 13:05:11 -04:00
Mayukha Vadari
fb1311e013 uncomment???? 2025-05-28 14:00:50 -04:00
Mayukha Vadari
ce31acf030 debug comments 2025-05-28 13:48:38 -04:00
Mayukha Vadari
31ad5ac63b Merge remote-tracking branch 'upstream/ripple/smart-escrow' into develop3 2025-05-27 18:29:41 -04:00
Mayukha Vadari
1ede0bdec4 fix: fix fixtures (#5445) 2025-05-23 17:37:14 -04:00
Mayukha Vadari
aef32ead2c better WASM logging to match rippled (#5395)
* basic logging

* pass in Journal

* log level based on journal level

* clean up

* attempt at adding WAMR logging properly

* improve logline

* maybe_unused

* fix

* fix

* fix segfault

* add test
2025-05-23 10:31:02 -04:00
Mayukha Vadari
5b43ec7f73 refactor: switch function name from ready to finish (#5430) 2025-05-20 16:12:19 -04:00
Olek
1e9ff88a00 Fix CI build issues
* Mac build fix
* Windows build fix
* Windows instruction counter fix
2025-05-08 12:39:37 -04:00
Mayukha Vadari
bb9bb5f5c5 Merge branch 'ripple/smart-escrow' into develop2 2025-05-01 18:44:06 -04:00
Mayukha Vadari
c533abd8b6 Update size and compute cap defaults (#5417) 2025-05-01 18:41:51 -04:00
Olek
bb9bc764bc Switch to WAMR (#5416)
* Switch to WAMR
2025-05-01 18:02:06 -04:00
Mayukha Vadari
b4b53a6cb7 Merge branch 'ripple/smart-escrow' into develop2 2025-04-29 15:25:54 -04:00
Mayukha Vadari
9c0204906c fix reference fee tests 2025-04-29 15:25:00 -04:00
Mayukha Vadari
4670b373c1 try to fix tests 2025-04-29 14:10:27 -04:00
Mayukha Vadari
f03b5883bd More host functions (#5411)
* getNFT

* escrow keylet

* account keylet

* credential keylet

* oracle keylet

* hook everything in

* fix stuff
2025-04-29 12:39:12 -04:00
Mayukha Vadari
f8b2fe4dd5 fix imports 2025-04-28 17:43:15 -04:00
Mayukha Vadari
be4a0c9c2b Merge remote-tracking branch 'upstream/ripple/smart-escrow' into develop2 2025-04-28 17:14:28 -04:00
Mayukha Vadari
f37d52d8e9 Set up fees for WASM processing (#5393)
* set up fields

* throw error if allowance is too high

* votable gas price

* fix comments

* hook everything together

* make test less flaky (hopefully)

* fix other tests

* fix some tests

* fix tests

* clean up

* add more tests

* uncomment other tests

* respond to comments

* fix build

* respond to comments
2025-04-24 08:47:13 -04:00
Mayukha Vadari
177cdaf550 Connect votable gas limit into VM (#5360)
* [WIP] add gas limit

* [WIP] host function escrow tests

* finish test

* uncomment out tests
2025-03-25 10:55:33 -04:00
pwang200
1573a443b7 smart escrow devnet 1 host functions (#5353)
* devnet 1 host functions

* clang-format

* fix build issues
2025-03-24 17:07:17 -04:00
Mayukha Vadari
911c0466c0 Merge develop into ripple/smart-escrow (#5357)
* Set version to 2.4.0

* refactor: Remove unused and add missing includes (#5293)

The codebase is filled with includes that are unused, and which thus can be removed. At the same time, the files often do not include all headers that contain the definitions used in those files. This change uses clang-format and clang-tidy to clean up the includes, with minor manual intervention to ensure the code compiles on all platforms.

* refactor: Calculate numFeatures automatically (#5324)

Requiring manual updates of numFeatures is an annoying manual process that is easily forgotten, and leads to frequent merge conflicts. This change takes advantage of the `XRPL_FEATURE` and `XRPL_FIX` macros, and adds a new `XRPL_RETIRE` macro to automatically set `numFeatures`.

* refactor: Improve ordering of headers with clang-format (#5343)

Removes all manual header groupings from source and header files by leveraging clang-format options.

* Rename "deadlock" to "stall" in `LoadManager` (#5341)

What the LoadManager class does is stall detection, which is not the same as deadlock detection. In the condition of severe CPU starvation, LoadManager will currently intentionally crash rippled reporting `LogicError: Deadlock detected`. This error message is misleading as the condition being detected is not a deadlock. This change fixes and refactors the code in response.

* Adds hub.xrpl-commons.org as a new Bootstrap Cluster (#5263)

* fix: Error message for ledger_entry rpc (#5344)

Changes the error to `malformedAddress` for `permissioned_domain` in the `ledger_entry` rpc, when the account is not a string. This change makes it more clear to a user what is wrong with their request.

* fix: Handle invalid marker parameter in grpc call (#5317)

The `end_marker` is used to limit the range of ledger entries to fetch. If `end_marker` is less than `marker`, a crash can occur. This change adds an additional check.

* fix: trust line RPC no ripple flag (#5345)

The Trustline RPC `no_ripple` flag gets set depending on `lsfDefaultRipple` flag, which is not a flag of a trustline but of the account root. The `lsfDefaultRipple` flag does not provide any insight if this particular trust line has `lsfLowNoRipple` or `lsfHighNoRipple` flag set, so it should not be used here at all. This change simplifies the logic.

* refactor: Updates Conan dependencies: RocksDB (#5335)

Updates RocksDB to version 9.7.3, the latest version supported in Conan 1.x. A patch for 9.7.4 that fixes a memory leak is included.

* fix: Remove null pointer deref, just do abort (#5338)

This change removes the existing undefined behavior from `LogicError`, so we can be certain that there will be always a stacktrace.

De-referencing a null pointer is an old trick to generate `SIGSEGV`, which would typically also create a stacktrace. However it is also an undefined behaviour and compilers can do something else. A more robust way to create a stacktrace while crashing the program is to use `std::abort`, which we have also used in this location for a long time. If we combine the two, we might not get the expected behaviour - namely, the nullpointer deref followed by `std::abort`, as handled in certain compiler versions may not immediately cause a crash. We have observed stacktrace being wiped instead, and thread put in indeterminate state, then stacktrace created without any useful information.

* chore: Add PR number to payload (#5310)

This PR adds one more payload field to the libXRPL compatibility check workflow - the PR number itself.

* chore: Update link to ripple-binary-codec (#5355)

The link to ripple-binary-codec's definitions.json appears to be outdated. The updated link is also documented here: https://xrpl.org/docs/references/protocol/binary-format#definitions-file

* Prevent consensus from getting stuck in the establish phase (#5277)

- Detects if the consensus process is "stalled". If it is, then we can declare a 
  consensus and end successfully even if we do not have 80% agreement on
  our proposal.
  - "Stalled" is defined as:
    - We have a close time consensus
    - Each disputed transaction is individually stalled:
      - It has been in the final "stuck" 95% requirement for at least 2
        (avMIN_ROUNDS) "inner rounds" of phaseEstablish,
      - and either all of the other trusted proposers or this validator, if proposing,
        have had the same vote(s) for at least 4 (avSTALLED_ROUNDS) "inner
        rounds", and at least 80% of the validators (including this one, if
        appropriate) agree about the vote (whether yes or no).
- If we have been in the establish phase for more than 10x the previous
  consensus establish phase's time, then consensus is considered "expired",
  and we will leave the round, which sends a partial validation (indicating
  that the node is moving on without validating). Two restrictions avoid
  prematurely exiting, or having an extended exit in extreme situations.
  - The 10x time is clamped to be within a range of 15s
    (ledgerMAX_CONSENSUS) to 120s (ledgerABANDON_CONSENSUS).
  - If consensus has not had an opportunity to walk through all avalanche
    states (defined as not going through 8 "inner rounds" of phaseEstablish),
    then ConsensusState::Expired is treated as ConsensusState::No.
- When enough nodes leave the round, any remaining nodes will see they've
  fallen behind, and move on, too, generally before hitting the timeout. Any
  validations or partial validations sent during this time will help the
  consensus process bring the nodes back together.

---------

Co-authored-by: Michael Legleux <mlegleux@ripple.com>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Co-authored-by: Ed Hennis <ed@ripple.com>
Co-authored-by: Bronek Kozicki <brok@incorrekt.com>
Co-authored-by: Darius Tumas <Tokeiito@users.noreply.github.com>
Co-authored-by: Sergey Kuznetsov <skuznetsov@ripple.com>
Co-authored-by: cyan317 <120398799+cindyyan317@users.noreply.github.com>
Co-authored-by: Vlad <129996061+vvysokikh1@users.noreply.github.com>
Co-authored-by: Alex Kremer <akremer@ripple.com>
2025-03-20 16:47:14 -04:00
Mayukha Vadari
b6a95f9970 PoC Smart Escrows (#5340)
* wasmedge in unittest

* add WashVM.h and cpp

* accountID comparison (vector<u8>) working

* json decode tx and ledger object with two buffers working

* wasm return a buffer working

* add a failure test case to P2P3

* host function return ledger sqn

* instruction gas and host function gas

* basics

* add scaffold

* add amendment check

* working PoC

* get test working

* fix clang-format

* prototype #2

* p2p3

* [WIP] P4

* P5

* add calculateBaseFee

* add FinishFunction preflight checks (+ tests)

* additional reserve for sfFinishFunction

* higher fees for EscrowFinish

* rename amendment to SmartEscrow

* make fee voting changes, add basic tests

* clean up

* clean up

* clean up

* more cleanup

* add subscribe tests

* add more tests

* undo formatting

* undo formatting

* remove bad comment

* more debugging statements

* fix clang-format

* fix rebase issues

* fix more rebase issues

* more rebase fixes

* add source code for wasm

* respond to comments

* add const

---------

Co-authored-by: Peng Wang <pwang200@gmail.com>
2025-03-20 14:08:06 -04:00
41 changed files with 2496 additions and 121 deletions

View File

@@ -6,6 +6,7 @@
#include <xrpl/basics/TaggedCache.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/Fees.h>
#include <boost/asio.hpp>
@@ -246,6 +247,9 @@ public:
virtual DatabaseCon&
getWalletDB() = 0;
[[nodiscard]] virtual Fees
getFees() const = 0;
// Temporary: Get the underlying Application for functions that haven't
// been migrated yet. This should be removed once all code is migrated.
virtual Application&

View File

@@ -13,6 +13,7 @@
#include <xrpl/protocol/TxMeta.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <optional>
@@ -68,6 +69,18 @@ public:
deliver_ = amount;
}
void
setGasUsed(std::optional<std::uint32_t> const gasUsed)
{
gasUsed_ = gasUsed;
}
void
setVMReturnCode(std::int32_t const vmReturnCode)
{
vmReturnCode_ = vmReturnCode;
}
/**
* Get the number of modified entries
*/
@@ -88,6 +101,8 @@ public:
private:
std::optional<STAmount> deliver_;
std::optional<std::uint32_t> gasUsed_;
std::optional<std::int32_t> vmReturnCode_;
};
} // namespace xrpl

View File

@@ -16,6 +16,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <optional>
@@ -62,6 +63,8 @@ public:
TER ter,
std::optional<STAmount> const& deliver,
std::optional<uint256 const> const& parentBatchId,
std::optional<std::uint32_t> const& gasUsed,
std::optional<std::int32_t> const& vmReturnCode,
bool isDryRun,
beast::Journal j);

View File

@@ -25,6 +25,8 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
namespace xrpl {
template <ValidIssueType T>
@@ -272,4 +274,15 @@ escrowUnlockApplyHelper<MPTIssue>(
journal);
}
template <class T>
static int32_t
calculateAdditionalReserve(T const& finishFunction)
{
if (!finishFunction)
return 1;
// First 500 bytes included in the normal reserve
// Each additional 500 bytes requires an additional reserve
return 1 + (finishFunction->size() / 500);
}
} // namespace xrpl

View File

@@ -237,6 +237,12 @@ page(Keylet const& root, std::uint64_t const index = 0) noexcept
Keylet
escrow(AccountID const& src, SeqProxy const& seq) noexcept;
inline Keylet
escrow(uint256 const& key) noexcept
{
return {ltESCROW, key};
}
/**
* A PaymentChannel
*/

View File

@@ -133,6 +133,8 @@ enum TEMcodes : TERUnderlyingType {
temBAD_MPT,
temBAD_CIPHERTEXT,
temBAD_WASM,
temINVALID_BYTECODE,
temTEMP_DISABLED,
};
//------------------------------------------------------------------------------
@@ -181,6 +183,8 @@ enum TEFcodes : TERUnderlyingType {
tefINVALID_LEDGER_FIX_TYPE,
tefNO_DST_PARTIAL,
tefBAD_PATH_COUNT,
tefNO_BYTECODE,
tefBYTECODE_NOT_INCLUDED,
};
//------------------------------------------------------------------------------
@@ -373,6 +377,7 @@ enum TECcodes : TERUnderlyingType {
tecBAD_PROOF = 199,
tecNO_SPONSOR_PERMISSION = 200,
tecOUT_OF_GAS = 201,
tecBYTECODE_REJECTED = 202,
};
//------------------------------------------------------------------------------

View File

@@ -95,6 +95,12 @@ public:
if (obj.isFieldPresent(sfParentBatchID))
parentBatchID_ = obj.getFieldH256(sfParentBatchID);
if (obj.isFieldPresent(sfGasUsed))
gasUsed_ = obj.getFieldU32(sfGasUsed);
if (obj.isFieldPresent(sfVMReturnCode))
vmReturnCode_ = obj.getFieldI32(sfVMReturnCode);
}
[[nodiscard]] std::optional<STAmount> const&
@@ -115,6 +121,30 @@ public:
parentBatchID_ = id;
}
void
setGasUsed(std::optional<std::uint32_t> const gasUsed)
{
gasUsed_ = gasUsed;
}
[[nodiscard]] std::optional<std::uint32_t> const&
getGasUsed() const
{
return gasUsed_;
}
void
setVMReturnCode(std::optional<std::int32_t> const vmReturnCode)
{
vmReturnCode_ = vmReturnCode;
}
[[nodiscard]] std::optional<std::int32_t> const&
getVMReturnCode() const
{
return vmReturnCode_;
}
private:
uint256 transactionID_;
std::uint32_t ledgerSeq_;
@@ -123,6 +153,8 @@ private:
std::optional<STAmount> deliveredAmount_;
std::optional<uint256> parentBatchID_;
std::optional<std::uint32_t> gasUsed_;
std::optional<std::int32_t> vmReturnCode_;
STArray nodes_;
};

View File

@@ -16,7 +16,7 @@
// Keep it sorted in reverse chronological order.
XRPL_FIX (Cleanup3_4_0, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(SmartEscrow, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(SmartEscrow, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Sponsor, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(BatchV1_1, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocolV1_1, Supported::No, VoteBehavior::DefaultNo)

View File

@@ -344,6 +344,8 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
{sfCondition, SoeOptional},
{sfCancelAfter, SoeOptional},
{sfFinishAfter, SoeOptional},
{sfBytecode, SoeOptional},
{sfData, SoeOptional},
{sfSourceTag, SoeOptional},
{sfDestinationTag, SoeOptional},
{sfOwnerNode, SoeRequired},

View File

@@ -123,6 +123,8 @@ TYPED_SFIELD(sfSponsorFlags, UINT32, 74)
TYPED_SFIELD(sfGasLimit, UINT32, 75)
TYPED_SFIELD(sfBytecodeSizeLimit, UINT32, 76)
TYPED_SFIELD(sfGasPrice, UINT32, 77)
TYPED_SFIELD(sfGas, UINT32, 78)
TYPED_SFIELD(sfGasUsed, UINT32, 79)
// 64-bit integers (common)
TYPED_SFIELD(sfIndexNext, UINT64, 1)
@@ -240,9 +242,10 @@ TYPED_SFIELD(sfTotalValueOutstanding, NUMBER, 15, SField::kSmdNeedsAsset
TYPED_SFIELD(sfPeriodicPayment, NUMBER, 16)
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17, SField::kSmdNeedsAsset | SField::kSmdDefault)
// int32
// 32-bit signed (common)
TYPED_SFIELD(sfLoanScale, INT32, 1)
TYPED_SFIELD(sfRemainingOwnerCountDelta, INT32, 2)
TYPED_SFIELD(sfVMReturnCode, INT32, 3)
// currency amount (common)
TYPED_SFIELD(sfAmount, AMOUNT, 1)
@@ -272,7 +275,7 @@ TYPED_SFIELD(sfBaseFeeDrops, AMOUNT, 22)
TYPED_SFIELD(sfReserveBaseDrops, AMOUNT, 23)
TYPED_SFIELD(sfReserveIncrementDrops, AMOUNT, 24)
// currency amount (AMM)
// currency amount (more)
TYPED_SFIELD(sfLPTokenOut, AMOUNT, 25)
TYPED_SFIELD(sfLPTokenIn, AMOUNT, 26)
TYPED_SFIELD(sfEPrice, AMOUNT, 27)
@@ -332,6 +335,7 @@ TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43)
TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
TYPED_SFIELD(sfBytecode, VL, 47)
// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)

View File

@@ -50,11 +50,13 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
NoPriv,
({
{sfDestination, SoeRequired},
{sfDestinationTag, SoeOptional},
{sfAmount, SoeRequired, SoeMptSupported},
{sfCondition, SoeOptional},
{sfCancelAfter, SoeOptional},
{sfFinishAfter, SoeOptional},
{sfDestinationTag, SoeOptional},
{sfBytecode, SoeOptional},
{sfData, SoeOptional},
}))
/** This transaction type completes an existing escrow. */
@@ -71,6 +73,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
{sfFulfillment, SoeOptional},
{sfCondition, SoeOptional},
{sfCredentialIDs, SoeOptional},
{sfGas, SoeOptional},
}))

View File

@@ -174,6 +174,54 @@ public:
return this->sle_->isFieldPresent(sfFinishAfter);
}
/**
* @brief Get sfBytecode (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getBytecode() const
{
if (hasBytecode())
return this->sle_->at(sfBytecode);
return std::nullopt;
}
/**
* @brief Check if sfBytecode is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasBytecode() const
{
return this->sle_->isFieldPresent(sfBytecode);
}
/**
* @brief Get sfData (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getData() const
{
if (hasData())
return this->sle_->at(sfData);
return std::nullopt;
}
/**
* @brief Check if sfData is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasData() const
{
return this->sle_->isFieldPresent(sfData);
}
/**
* @brief Get sfSourceTag (SoeOptional)
* @return The field value, or std::nullopt if not present.
@@ -453,6 +501,28 @@ public:
return *this;
}
/**
* @brief Set sfBytecode (SoeOptional)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setBytecode(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfBytecode] = value;
return *this;
}
/**
* @brief Set sfData (SoeOptional)
* @return Reference to this builder for method chaining.
*/
EscrowBuilder&
setData(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfData] = value;
return *this;
}
/**
* @brief Set sfSourceTag (SoeOptional)
* @return Reference to this builder for method chaining.

View File

@@ -58,6 +58,32 @@ public:
return this->tx_->at(sfDestination);
}
/**
* @brief Get sfDestinationTag (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getDestinationTag() const
{
if (hasDestinationTag())
{
return this->tx_->at(sfDestinationTag);
}
return std::nullopt;
}
/**
* @brief Check if sfDestinationTag is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasDestinationTag() const
{
return this->tx_->isFieldPresent(sfDestinationTag);
}
/**
* @brief Get sfAmount (SoeRequired)
* @note This field supports MPT (Multi-Purpose Token) amounts.
@@ -149,29 +175,55 @@ public:
}
/**
* @brief Get sfDestinationTag (SoeOptional)
* @brief Get sfBytecode (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getDestinationTag() const
protocol_autogen::Optional<SF_VL::type::value_type>
getBytecode() const
{
if (hasDestinationTag())
if (hasBytecode())
{
return this->tx_->at(sfDestinationTag);
return this->tx_->at(sfBytecode);
}
return std::nullopt;
}
/**
* @brief Check if sfDestinationTag is present.
* @brief Check if sfBytecode is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasDestinationTag() const
hasBytecode() const
{
return this->tx_->isFieldPresent(sfDestinationTag);
return this->tx_->isFieldPresent(sfBytecode);
}
/**
* @brief Get sfData (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getData() const
{
if (hasData())
{
return this->tx_->at(sfData);
}
return std::nullopt;
}
/**
* @brief Check if sfData is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasData() const
{
return this->tx_->isFieldPresent(sfData);
}
};
@@ -232,6 +284,17 @@ public:
return *this;
}
/**
* @brief Set sfDestinationTag (SoeOptional)
* @return Reference to this builder for method chaining.
*/
EscrowCreateBuilder&
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfDestinationTag] = value;
return *this;
}
/**
* @brief Set sfAmount (SoeRequired)
* @note This field supports MPT (Multi-Purpose Token) amounts.
@@ -278,13 +341,24 @@ public:
}
/**
* @brief Set sfDestinationTag (SoeOptional)
* @brief Set sfBytecode (SoeOptional)
* @return Reference to this builder for method chaining.
*/
EscrowCreateBuilder&
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
setBytecode(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfDestinationTag] = value;
object_[sfBytecode] = value;
return *this;
}
/**
* @brief Set sfData (SoeOptional)
* @return Reference to this builder for method chaining.
*/
EscrowCreateBuilder&
setData(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfData] = value;
return *this;
}

View File

@@ -146,6 +146,32 @@ public:
{
return this->tx_->isFieldPresent(sfCredentialIDs);
}
/**
* @brief Get sfGas (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT32::type::value_type>
getGas() const
{
if (hasGas())
{
return this->tx_->at(sfGas);
}
return std::nullopt;
}
/**
* @brief Check if sfGas is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasGas() const
{
return this->tx_->isFieldPresent(sfGas);
}
};
/**
@@ -249,6 +275,17 @@ public:
return *this;
}
/**
* @brief Set sfGas (SoeOptional)
* @return Reference to this builder for method chaining.
*/
EscrowFinishBuilder&
setGas(std::decay_t<typename SF_UINT32::type::value_type> const& value)
{
object_[sfGas] = value;
return *this;
}
/**
* @brief Build and return the EscrowFinish wrapper.
* @param publicKey The public key for signing.

View File

@@ -15,6 +15,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <optional>
#include <utility>
@@ -95,6 +96,24 @@ public:
view_->deliver(amount);
}
/**
* Sets the gas used in the metadata
*/
void
setGasUsed(std::uint32_t const gasUsed)
{
gasUsed_ = gasUsed;
}
/**
* Sets the gas used in the metadata
*/
void
setVMReturnCode(std::int32_t const vmReturnCode)
{
vmReturnCode_ = vmReturnCode;
}
/**
* Discard changes and start fresh.
*/
@@ -163,6 +182,8 @@ private:
// The ID of the batch transaction we are executing under, if set.
std::optional<uint256 const> parentBatchId_;
std::optional<std::uint32_t> gasUsed_;
std::optional<std::int32_t> vmReturnCode_;
};
} // namespace xrpl

View File

@@ -20,15 +20,21 @@ public:
{
}
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static bool
checkExtraFeatures(PreflightContext const& ctx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static NotTEC
preflight(PreflightContext const& ctx);
static NotTEC
preflightSigValidated(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -113,6 +113,8 @@ ApplyStateTable::apply(
TER ter,
std::optional<STAmount> const& deliver,
std::optional<uint256 const> const& parentBatchId,
std::optional<std::uint32_t> const& gasUsed,
std::optional<std::int32_t> const& vmReturnCode,
bool isDryRun,
beast::Journal j)
{
@@ -127,6 +129,8 @@ ApplyStateTable::apply(
meta.setDeliveredAmount(deliver);
meta.setParentBatchID(parentBatchId);
meta.setGasUsed(gasUsed);
meta.setVMReturnCode(vmReturnCode);
Mods newMod;
for (auto& item : items_)

View File

@@ -30,7 +30,7 @@ ApplyViewImpl::apply(
bool isDryRun,
beast::Journal j)
{
return items_.apply(to, tx, ter, deliver_, parentBatchId, isDryRun, j);
return items_.apply(to, tx, ter, deliver_, parentBatchId, gasUsed_, vmReturnCode_, isDryRun, j);
}
std::size_t

View File

@@ -109,6 +109,7 @@ transResults()
MAKE_ERROR(tecBAD_PROOF, "Proof cannot be verified"),
MAKE_ERROR(tecNO_SPONSOR_PERMISSION, "Sponsor has not authorized this transaction."),
MAKE_ERROR(tecOUT_OF_GAS, "The WASM code ran out of gas during execution."),
MAKE_ERROR(tecBYTECODE_REJECTED, "The custom WASM code that was run rejected your transaction."),
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
@@ -134,6 +135,8 @@ transResults()
MAKE_ERROR(tefINVALID_LEDGER_FIX_TYPE, "The LedgerFixType field has an invalid value."),
MAKE_ERROR(tefNO_DST_PARTIAL, "Partial payment to create account not allowed."),
MAKE_ERROR(tefBAD_PATH_COUNT, "Malformed: Too many paths."),
MAKE_ERROR(tefNO_BYTECODE, "There is no WASM code to run, but a WASM-specific field was included."),
MAKE_ERROR(tefBYTECODE_NOT_INCLUDED, "WASM code requires a field to be included that was not included."),
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),
@@ -206,6 +209,8 @@ transResults()
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."),
MAKE_ERROR(temBAD_CIPHERTEXT, "Malformed: Invalid ciphertext."),
MAKE_ERROR(temBAD_WASM, "Malformed: Provided WASM code is invalid."),
MAKE_ERROR(temINVALID_BYTECODE, "Malformed: Provided WASM code is invalid."),
MAKE_ERROR(temTEMP_DISABLED, "The transaction requires logic that is currently temporarily disabled."),
MAKE_ERROR(terRETRY, "Retry transaction."),
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),

View File

@@ -194,6 +194,12 @@ TxMeta::getAsObject() const
if (parentBatchID_.has_value())
metaData.setFieldH256(sfParentBatchID, *parentBatchID_);
if (gasUsed_.has_value())
metaData.setFieldU32(sfGasUsed, *gasUsed_);
if (vmReturnCode_.has_value())
metaData.setFieldI32(sfVMReturnCode, *vmReturnCode_);
return metaData;
}

View File

@@ -58,6 +58,18 @@ ApplyContext::discard()
std::optional<TxMeta>
ApplyContext::apply(TER ter)
{
// tecINTERNAL reports an xrpld bug, not a result: nothing the VM recorded
// before we hit it belongs in the metadata.
if (ter != tecINTERNAL)
{
if (vmReturnCode_.has_value())
{
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
view_->setVMReturnCode(*vmReturnCode_);
}
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
view_->setGasUsed(gasUsed_);
}
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & TapDryRun) != 0u, journal);
}

View File

@@ -1284,6 +1284,23 @@ removeDeletedTrustLines(
}
}
static void
modifyWasmDataFields(
ApplyView& view,
std::vector<std::pair<uint256, SLE::const_pointer>> const& wasmObjects,
beast::Journal viewJ)
{
for (auto const& [index, after] : wasmObjects)
{
if (auto const sle = view.peek(keylet::escrow(index)))
{
auto const data = after->getFieldVL(sfData);
sle->setFieldVL(sfData, data);
view.update(sle);
}
}
}
/**
* Reset the context, discarding any changes made and adjust the fee.
*
@@ -1451,6 +1468,10 @@ Transactor::processPersistentChanges(TER result, XRPAmount fee)
types.insert(ltNFTOKEN_OFFER);
types.insert(ltCREDENTIAL);
}
else if (ter == tecBYTECODE_REJECTED)
{
types.insert(ltESCROW);
}
return types;
};
@@ -1460,10 +1481,11 @@ Transactor::processPersistentChanges(TER result, XRPAmount fee)
auto const typesToCollect = typesForResult(result);
std::map<LedgerEntryType, std::vector<uint256>> deletedObjects;
std::map<LedgerEntryType, std::vector<std::pair<uint256, SLE::const_pointer>>> modifiedObjects;
if (!typesToCollect.empty())
{
ctx_.visit(
[&typesToCollect, &deletedObjects](
[&typesToCollect, &deletedObjects, &modifiedObjects](
uint256 const& index, bool isDelete, SLE::const_ref before, SLE::const_ref after) {
if (isDelete)
{
@@ -1487,6 +1509,16 @@ Transactor::processPersistentChanges(TER result, XRPAmount fee)
}
}
}
else if (after)
{
// Collect modified escrows so that data written by a
// rejected WASM execution can be re-applied after the
// context is reset.
auto const type = after->getType();
if (typesToCollect.contains(type) && type == ltESCROW &&
after->isFieldPresent(sfData))
modifiedObjects[type].emplace_back(index, after);
}
});
}
@@ -1533,6 +1565,24 @@ Transactor::processPersistentChanges(TER result, XRPAmount fee)
// LCOV_EXCL_STOP
}
}
for (auto const& [type, ids] : modifiedObjects)
{
if (ids.empty() || !typesToApply.contains(type))
continue;
switch (type)
{
case ltESCROW:
modifyWasmDataFields(view(), ids, viewJ);
break;
// LCOV_EXCL_START
default:
UNREACHABLE(
"xrpl::Transactor::processPersistentChanges() : "
"unexpected type");
break;
// LCOV_EXCL_STOP
}
}
}
return {result, fee, isTecClaim(result)};
@@ -1652,7 +1702,8 @@ Transactor::operator()()
}
else if (
(result == tecOVERSIZE) || (result == tecKILLED) || (result == tecINCOMPLETE) ||
(result == tecEXPIRED) || (isTecClaimHardFail(result, view().flags())))
(result == tecEXPIRED) || (result == tecBYTECODE_REJECTED) ||
(isTecClaimHardFail(result, view().flags())))
{
std::tie(result, fee, applied) = processPersistentChanges(result, fee);
}

View File

@@ -212,7 +212,8 @@ EscrowCancel::doApply()
}
}
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal);
auto const reserveToSubtract = calculateAdditionalReserve((*slep)[~sfBytecode]);
decreaseOwnerCountForObject(ctx_.view(), sle, slep, reserveToSubtract, ctx_.journal);
// Remove escrow from ledger
ctx_.view().erase(slep);

View File

@@ -6,9 +6,11 @@
#include <xrpl/conditions/Condition.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -32,6 +34,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/wasm/HostFunc.h>
#include <xrpl/tx/wasm/WasmVM.h>
#include <memory>
#include <system_error>
@@ -88,8 +92,13 @@ EscrowCreate::checkExtraFeatures(PreflightContext const& ctx)
// Only require featureMPTokensV1 when the escrow amount is an MPT and
// fixCleanup3_2_0 is active; XRP/IOU escrows are unaffected by this gate.
if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfAmount].holds<MPTIssue>())
return ctx.rules.enabled(featureMPTokensV1);
return true;
{
if (!ctx.rules.enabled(featureMPTokensV1))
return false;
}
return (!ctx.tx.isFieldPresent(sfBytecode) && !ctx.tx.isFieldPresent(sfData)) ||
ctx.rules.enabled(featureSmartEscrow);
}
template <ValidIssueType T>
@@ -124,6 +133,19 @@ escrowCreatePreflightHelper<MPTIssue>(PreflightContext const& ctx)
return tesSUCCESS;
}
XRPAmount
EscrowCreate::calculateBaseFee(ReadView const& view, STTx const& tx)
{
XRPAmount txnFees{Transactor::calculateBaseFee(view, tx)};
if (tx.isFieldPresent(sfBytecode))
{
// 10 base fees for the transaction (1 is in
// `Transactor::calculateBaseFee`), plus 5 drops per byte
txnFees += 9 * view.fees().base + 5 * tx[sfBytecode].size();
}
return txnFees;
}
NotTEC
EscrowCreate::preflight(PreflightContext const& ctx)
{
@@ -155,12 +177,19 @@ EscrowCreate::preflight(PreflightContext const& ctx)
ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter])
return temBAD_EXPIRATION;
if (ctx.tx.isFieldPresent(sfBytecode) && !ctx.tx.isFieldPresent(sfCancelAfter))
return temBAD_EXPIRATION;
// In the absence of a FinishAfter, the escrow can be finished
// immediately, which can be confusing. When creating an escrow,
// we want to ensure that either a FinishAfter time is explicitly
// specified or a completion condition is attached.
if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition])
if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition] && !ctx.tx[~sfBytecode])
{
JLOG(ctx.j.debug()) << "Must have at least one of FinishAfter, "
"Condition, or Bytecode.";
return temMALFORMED;
}
if (auto const cb = ctx.tx[~sfCondition])
{
@@ -176,6 +205,60 @@ EscrowCreate::preflight(PreflightContext const& ctx)
}
}
if (ctx.tx.isFieldPresent(sfData))
{
if (!ctx.tx.isFieldPresent(sfBytecode))
{
JLOG(ctx.j.debug()) << "EscrowCreate with Data requires Bytecode";
return temMALFORMED;
}
auto const data = ctx.tx.getFieldVL(sfData);
if (data.size() > kMaxWasmDataLength)
{
JLOG(ctx.j.debug()) << "EscrowCreate.Data bad size " << data.size();
return temMALFORMED;
}
}
if (ctx.tx.isFieldPresent(sfBytecode))
{
auto const fees(ctx.registry.get().getFees());
if (fees.bytecodeSizeLimit == 0 || fees.gasLimit == 0)
{
JLOG(ctx.j.debug()) << "WASM runtime deactivated by fee voting";
return temTEMP_DISABLED;
}
auto const code = ctx.tx.getFieldVL(sfBytecode);
if (code.empty() || code.size() > fees.bytecodeSizeLimit)
{
JLOG(ctx.j.debug()) << "EscrowCreate.Bytecode bad size " << code.size();
return temMALFORMED;
}
// actual validity of WASM code happens in `preflightSigValidated`
// (after the signature is checked)
}
return tesSUCCESS;
}
NotTEC
EscrowCreate::preflightSigValidated(PreflightContext const& ctx)
{
if (ctx.tx.isFieldPresent(sfBytecode))
{
auto const code = ctx.tx.getFieldVL(sfBytecode);
// basic checks happen in `preflight`
HostFunctions mock(ctx.j);
auto const re = preflightEscrowWasm(code, mock, escrowFunctionName);
if (!isTesSuccess(re))
{
JLOG(ctx.j.debug()) << "EscrowCreate.Bytecode bad WASM";
return re;
}
}
return tesSUCCESS;
}
@@ -434,6 +517,7 @@ EscrowCreate::doApply()
// Check reserve and funds availability
STAmount const amount{ctx_.tx[sfAmount]};
auto const reserveToAdd = calculateAdditionalReserve(ctx_.tx[~sfBytecode]);
auto const balance = sle->getFieldAmount(sfBalance).xrp();
// First check: whoever is on the hook for the new owner increment
@@ -441,8 +525,8 @@ EscrowCreate::doApply()
// validates the sponsor's reserve + remaining credit. When
// unsponsored this hits the source branch and validates the
// source's pre-lock balance against base + (currentOC+1)*increment.
if (auto const ret =
checkReserve(ctx_.getApplyViewContext(), sle, balance, {.ownerCountDelta = 1}, j_);
if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), sle, balance, {.ownerCountDelta = reserveToAdd}, j_);
!isTesSuccess(ret))
return ret;
@@ -486,6 +570,8 @@ EscrowCreate::doApply()
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
(*slep)[~sfBytecode] = ctx_.tx[~sfBytecode];
(*slep)[~sfData] = ctx_.tx[~sfData];
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
{
@@ -553,7 +639,7 @@ EscrowCreate::doApply()
}
// increment owner count
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
increaseOwnerCount(ctx_.getApplyViewContext(), sle, reserveToAdd, ctx_.journal);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
ctx_.view().update(sle);
return tesSUCCESS;

View File

@@ -18,9 +18,11 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Concepts.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Fees.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAmount.h>
@@ -30,9 +32,16 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/wasm/HostFuncImpl.h>
#include <xrpl/tx/wasm/WasmVM.h>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <system_error>
#include <variant>
#include <vector>
namespace xrpl {
@@ -65,7 +74,13 @@ checkCondition(Slice f, Slice c)
bool
EscrowFinish::checkExtraFeatures(PreflightContext const& ctx)
{
return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials);
if (ctx.tx.isFieldPresent(sfCredentialIDs) && !ctx.rules.enabled(featureCredentials))
return false;
if (ctx.tx.isFieldPresent(sfGas) && !ctx.rules.enabled(featureSmartEscrow))
return false;
return true;
}
NotTEC
@@ -77,7 +92,32 @@ EscrowFinish::preflight(PreflightContext const& ctx)
// If you specify a condition, then you must also specify
// a fulfillment.
if (static_cast<bool>(cb) != static_cast<bool>(fb))
{
JLOG(ctx.j.debug()) << "Condition != Fulfillment";
return temMALFORMED;
}
if (auto const allowance = ctx.tx[~sfGas]; allowance)
{
auto const fees(ctx.registry.get().getFees());
if (fees.gasLimit == 0)
{
JLOG(ctx.j.debug()) << "WASM runtime deactivated by fee voting";
return temTEMP_DISABLED;
}
if (*allowance == 0)
{
return temBAD_LIMIT;
}
if (*allowance > fees.gasLimit)
{
JLOG(ctx.j.debug()) << "Gas too large: " << *allowance;
return temBAD_LIMIT;
}
}
if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err))
return err;
return tesSUCCESS;
}
@@ -111,9 +151,6 @@ EscrowFinish::preflightSigValidated(PreflightContext const& ctx)
}
}
if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err))
return err;
return tesSUCCESS;
}
@@ -126,7 +163,15 @@ EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx)
{
extraFee += view.fees().base * (32 + (fb->size() / 16));
}
if (std::optional<uint64_t> const allowance = tx[~sfGas]; allowance)
{
// The extra fee is the allowance in drops, rounded up to the nearest
// whole drop.
// Integer math rounds down by default, so we add 1 to round up.
uint64_t const allowanceFee =
(((*allowance) * view.fees().gasPrice) / microDropsPerDrop) + 1;
extraFee += allowanceFee;
}
return Transactor::calculateBaseFee(view, tx) + extraFee;
}
@@ -202,26 +247,51 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
return err;
}
if (ctx.view.rules().enabled(featureTokenEscrow))
if (ctx.view.rules().enabled(featureTokenEscrow) ||
ctx.view.rules().enabled(featureSmartEscrow))
{
// this check is done in doApply before this amendment is enabled
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy);
auto const slep = ctx.view.read(k);
if (!slep)
return tecNO_TARGET;
AccountID const dest = (*slep)[sfDestination];
STAmount const amount = (*slep)[sfAmount];
if (!isXRP(amount))
if (ctx.view.rules().enabled(featureSmartEscrow))
{
if (auto const ret = std::visit(
[&]<typename T>(T const&) {
return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
},
amount.asset().value());
!isTesSuccess(ret))
return ret;
if (slep->isFieldPresent(sfBytecode))
{
if (!ctx.tx.isFieldPresent(sfGas))
{
JLOG(ctx.j.debug()) << "Bytecode requires Gas";
return tefBYTECODE_NOT_INCLUDED;
}
}
else
{
if (ctx.tx.isFieldPresent(sfGas))
{
JLOG(ctx.j.debug()) << "Bytecode not present, "
"Gas present";
return tefNO_BYTECODE;
}
}
}
if (ctx.view.rules().enabled(featureTokenEscrow))
{
AccountID const dest = (*slep)[sfDestination];
STAmount const amount = (*slep)[sfAmount];
if (!isXRP(amount))
{
if (auto const ret = std::visit(
[&]<typename T>(T const&) {
return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
},
amount.asset().value());
!isTesSuccess(ret))
return ret;
}
}
}
return tesSUCCESS;
@@ -235,7 +305,8 @@ EscrowFinish::doApply()
auto const slep = ctx_.view().peek(k);
if (!slep)
{
if (ctx_.view().rules().enabled(featureTokenEscrow))
if (ctx_.view().rules().enabled(featureTokenEscrow) ||
ctx_.view().rules().enabled(featureSmartEscrow))
return tecINTERNAL; // LCOV_EXCL_LINE
return tecNO_TARGET;
@@ -253,6 +324,20 @@ EscrowFinish::doApply()
if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
return tecNO_PERMISSION;
AccountID const destID = (*slep)[sfDestination];
auto const sled = ctx_.view().peek(keylet::account(destID));
if (ctx_.view().rules().enabled(featureSmartEscrow))
{
// NOTE: Escrow payments cannot be used to fund accounts.
if (!sled)
return tecNO_DST;
if (auto err =
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal);
!isTesSuccess(err))
return err;
}
// Check cryptocondition fulfillment
{
auto const id = ctx_.tx.getTransactionID();
@@ -306,16 +391,78 @@ EscrowFinish::doApply()
return tecCRYPTOCONDITION_ERROR;
}
// NOTE: Escrow payments cannot be used to fund accounts.
AccountID const destID = (*slep)[sfDestination];
auto const sled = ctx_.view().peek(keylet::account(destID));
if (!sled)
return tecNO_DST;
if (!ctx_.view().rules().enabled(featureSmartEscrow))
{
// NOTE: Escrow payments cannot be used to fund accounts.
if (!sled)
return tecNO_DST;
if (auto err =
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal);
!isTesSuccess(err))
return err;
if (auto err =
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal);
!isTesSuccess(err))
return err;
}
// Execute custom release function
if ((*slep)[~sfBytecode])
{
JLOG(j_.trace()) << "The escrow has a finish function, running WASM code...";
// WASM execution
auto const wasmStr = slep->getFieldVL(sfBytecode);
std::vector<uint8_t> const wasm(wasmStr.begin(), wasmStr.end());
WasmHostFunctionsImpl ledgerDataProvider(ctx_, k);
if (!ctx_.tx.isFieldPresent(sfGas))
{
// already checked above, this check is just in case
return tecINTERNAL;
}
std::uint32_t const allowance = ctx_.tx[sfGas];
auto const re = runEscrowWasm(wasm, ledgerDataProvider, allowance, escrowFunctionName);
JLOG(j_.trace()) << "Escrow WASM ran";
// Gas consumed, reported in the tx metadata whenever the engine has a
// trustworthy number: a completed run, out of gas, or a wasm fault.
std::optional<std::int64_t> const cost = re.has_value() ? re->cost : re.error().cost;
if (cost.has_value())
{
// The engine cannot spend more than it was given, and pins the cost
// to the allowance when it runs out.
if (*cost < 0 || *cost > allowance)
return tecINTERNAL; // LCOV_EXCL_LINE
ctx_.setGasUsed(static_cast<std::uint32_t>(*cost));
}
if (!re.has_value())
{
// No return code, and any data it wrote goes away with the view.
JLOG(j_.debug()) << "WASM Failure: " + transHuman(re.error().ter);
return re.error().ter;
}
auto const reValue = re->result;
JLOG(j_.debug()) << "WASM Success: " + std::to_string(reValue) << ", cost: " << re->cost;
ctx_.setVMReturnCode(reValue);
// Only matters on a reject, where the escrow survives:
// Transactor::processPersistentChanges replays this after the reset.
if (auto const& data = ledgerDataProvider.getData(); data.has_value())
{
if (data->size() > kMaxWasmDataLength)
{
// should already be checked in the updateData host function
return tecINTERNAL; // LCOV_EXCL_LINE
}
slep->setFieldVL(sfData, makeSlice(*data));
ctx_.view().update(slep);
}
// 0 or negative is a contract-defined reject code, reported as sfVMReturnCode.
if (reValue <= 0)
return tecBYTECODE_REJECTED;
}
AccountID const account = (*slep)[sfAccount];
@@ -343,6 +490,8 @@ EscrowFinish::doApply()
}
}
auto const reserveToSubtract = calculateAdditionalReserve((*slep)[~sfBytecode]);
// With the Sponsor amendment, release the escrow reserve before delivery.
// Token delivery can auto-create a destination holding, and the same
// sponsor (or the same account, for a self-escrow) may cover both the
@@ -351,7 +500,7 @@ EscrowFinish::doApply()
// arithmetic for self-escrows and would break consensus if not gated.
bool const sponsorEnabled = ctx_.view().rules().enabled(featureSponsor);
if (sponsorEnabled)
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), account, slep, reserveToSubtract, ctx_.journal);
STAmount const amount = slep->getFieldAmount(sfAmount);
// Transfer amount to destination
@@ -404,7 +553,7 @@ EscrowFinish::doApply()
// Adjust source owner count (legacy position, pre-Sponsor)
if (!sponsorEnabled)
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), account, slep, reserveToSubtract, ctx_.journal);
// Remove escrow from ledger
ctx_.view().erase(slep);

View File

@@ -51,8 +51,8 @@ setCommonHostFunctions(HostFunctions& hfs, ImportVec& i)
WASM_IMPORT_FUNC2(i, getCurrentLedgerObjNestedArrayLen, "home_le_inner_arr_len", hfs, 70);
WASM_IMPORT_FUNC2(i, getLedgerObjNestedArrayLen, "le_inner_arr_len", hfs, 70);
WASM_IMPORT_FUNC2(i, checkSignature, "check_sig", hfs, 300);
WASM_IMPORT_FUNC2(i, computeSha512HalfHash, "sha512_half", hfs, 2000);
WASM_IMPORT_FUNC2(i, checkSignature, "check_sig", hfs, 35'000);
WASM_IMPORT_FUNC2(i, computeSha512HalfHash, "sha512_half", hfs, 2'000);
WASM_IMPORT_FUNC2(i, accountKeylet, "accountroot_id", hfs, 350);
WASM_IMPORT_FUNC2(i, ammKeylet, "amm_id", hfs, 450);

View File

@@ -903,7 +903,7 @@ WasmiEngine::check(
}
// LCOV_EXCL_STOP
return temBAD_WASM;
return temINVALID_BYTECODE;
}
NotTEC

View File

@@ -6747,7 +6747,7 @@ private:
using namespace test::jtx;
auto const testCase = [&](std::string suffix, FeatureBitset features) {
testcase("Fail pseudo-account allocation " + suffix);
testcase("Pseudo-account allocation failure " + suffix);
std::string logs;
Env env{*this, features, std::make_unique<CaptureLogs>(&logs)};
env.fund(XRP(30'000), gw_, alice_);

File diff suppressed because it is too large Load Diff

View File

@@ -1482,7 +1482,7 @@ struct Escrow_test : public beast::unit_test::Suite
Account const alice{"alice"};
Account const bob{"bob"};
Account const carol{"carol"};
Account const dillon{"dillon "};
Account const dillon{"dillon"};
Account const zelda{"zelda"};
char const credType[] = "abcde";
@@ -1639,6 +1639,8 @@ public:
FeatureBitset const all{testableAmendments()};
testWithFeats(all);
testWithFeats(all - featureTokenEscrow);
testWithFeats(all - featureSmartEscrow);
testWithFeats(all - featureTokenEscrow - featureSmartEscrow);
testTags(all - fixIncludeKeyletFields);
}
};

View File

@@ -135,7 +135,7 @@ getSection(Bytes const& module, std::uint8_t n)
}
static std::optional<int32_t>
runFinishFunction(std::string const& code)
runFinish(std::string const& code)
{
auto& engine = WasmEngine::instance();
auto const wasm = hexToBytes(code);
@@ -152,7 +152,7 @@ runFinishFunction(std::string const& code)
static bool
finishFunctionReturns(std::string const& code, int32_t expected)
{
auto const result = runFinishFunction(code);
auto const result = runFinish(code);
return result.has_value() && *result == expected;
}
@@ -253,7 +253,7 @@ struct Wasm_test : public beast::unit_test::Suite
}
{
// FinishFunction wrong function name
// Bytecode wrong function name
// pub fn bad() -> bool {
// unsafe { host_lib::getLedgerSqn() >= 5 }
// }
@@ -672,7 +672,7 @@ struct Wasm_test : public beast::unit_test::Suite
auto const codecovWasm = hexToBytes(kCodecovTestsWasmHex);
TestHostFunctions hfs(env);
auto const allowance = 125'667;
auto const allowance = 264'467;
auto re = runEscrowWasm(codecovWasm, hfs, allowance, escrowFunctionName, {});
checkResult(re, 1, allowance);
@@ -715,16 +715,16 @@ struct Wasm_test : public beast::unit_test::Suite
{
testcase("Wasm additional memory limit tests");
BEAST_EXPECT(finishFunctionReturns(kMemoryPointerAtLimitHex, 1));
BEAST_EXPECT(!runFinishFunction(kMemoryPointerOverLimitHex).has_value());
BEAST_EXPECT(!runFinishFunction(kMemoryOffsetOverLimitHex).has_value());
BEAST_EXPECT(!runFinishFunction(kMemoryEndOfWordOverLimitHex).has_value());
BEAST_EXPECT(!runFinish(kMemoryPointerOverLimitHex).has_value());
BEAST_EXPECT(!runFinish(kMemoryOffsetOverLimitHex).has_value());
BEAST_EXPECT(!runFinish(kMemoryEndOfWordOverLimitHex).has_value());
BEAST_EXPECT(finishFunctionReturns(kMemoryGrow0To1PageHex, 1));
BEAST_EXPECT(finishFunctionReturns(kMemoryGrow1To0PageHex, -1));
BEAST_EXPECT(finishFunctionReturns(kMemoryLastByteOf8MbHex, 1));
BEAST_EXPECT(finishFunctionReturns(kMemoryGrow1MoreThan8MbHex, -1));
BEAST_EXPECT(finishFunctionReturns(kMemoryGrow0MoreThan8MbHex, 1));
BEAST_EXPECT(!runFinishFunction(kMemoryInit1MoreThan8MbHex).has_value());
BEAST_EXPECT(!runFinishFunction(kMemoryNegativeAddressHex).has_value());
BEAST_EXPECT(!runFinish(kMemoryInit1MoreThan8MbHex).has_value());
BEAST_EXPECT(!runFinish(kMemoryNegativeAddressHex).has_value());
}
void
@@ -732,64 +732,64 @@ struct Wasm_test : public beast::unit_test::Suite
{
testcase("Wasm table limit tests");
BEAST_EXPECT(finishFunctionReturns(kTable64ElementsHex, 1));
BEAST_EXPECT(!runFinishFunction(kTable65ElementsHex).has_value());
BEAST_EXPECT(!runFinishFunction(kTable2TablesHex).has_value());
BEAST_EXPECT(!runFinish(kTable65ElementsHex).has_value());
BEAST_EXPECT(!runFinish(kTable2TablesHex).has_value());
BEAST_EXPECT(finishFunctionReturns(kTable0ElementsHex, 1));
BEAST_EXPECT(!runFinishFunction(kTableUintMaxHex).has_value());
BEAST_EXPECT(!runFinish(kTableUintMaxHex).has_value());
}
void
testWasmProposal()
{
testcase("Wasm disabled proposal tests");
BEAST_EXPECT(!runFinishFunction(kProposalMutableGlobalHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalGcStructNewHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalMultiValueHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalSignExtHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalFloatToIntHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalBulkMemoryHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalRefTypesHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalTailCallHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalExtendedConstHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalMultiMemoryHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalCustomPageSizesHex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalMemory64Hex).has_value());
BEAST_EXPECT(!runFinishFunction(kProposalWideArithmeticHex).has_value());
BEAST_EXPECT(!runFinish(kProposalMutableGlobalHex).has_value());
BEAST_EXPECT(!runFinish(kProposalGcStructNewHex).has_value());
BEAST_EXPECT(!runFinish(kProposalMultiValueHex).has_value());
BEAST_EXPECT(!runFinish(kProposalSignExtHex).has_value());
BEAST_EXPECT(!runFinish(kProposalFloatToIntHex).has_value());
BEAST_EXPECT(!runFinish(kProposalBulkMemoryHex).has_value());
BEAST_EXPECT(!runFinish(kProposalRefTypesHex).has_value());
BEAST_EXPECT(!runFinish(kProposalTailCallHex).has_value());
BEAST_EXPECT(!runFinish(kProposalExtendedConstHex).has_value());
BEAST_EXPECT(!runFinish(kProposalMultiMemoryHex).has_value());
BEAST_EXPECT(!runFinish(kProposalCustomPageSizesHex).has_value());
BEAST_EXPECT(!runFinish(kProposalMemory64Hex).has_value());
BEAST_EXPECT(!runFinish(kProposalWideArithmeticHex).has_value());
}
void
testWasmTrap()
{
testcase("Wasm trap tests");
BEAST_EXPECT(!runFinishFunction(kTrapDivideBy0Hex).has_value());
BEAST_EXPECT(!runFinishFunction(kTrapIntOverflowHex).has_value());
BEAST_EXPECT(!runFinishFunction(kTrapUnreachableHex).has_value());
BEAST_EXPECT(!runFinishFunction(kTrapNullCallHex).has_value());
BEAST_EXPECT(!runFinishFunction(kTrapFuncSigMismatchHex).has_value());
BEAST_EXPECT(!runFinish(kTrapDivideBy0Hex).has_value());
BEAST_EXPECT(!runFinish(kTrapIntOverflowHex).has_value());
BEAST_EXPECT(!runFinish(kTrapUnreachableHex).has_value());
BEAST_EXPECT(!runFinish(kTrapNullCallHex).has_value());
BEAST_EXPECT(!runFinish(kTrapFuncSigMismatchHex).has_value());
}
void
testWasmWasi()
{
testcase("Wasm Wasi tests");
BEAST_EXPECT(!runFinishFunction(kWasiGetTimeHex).has_value());
BEAST_EXPECT(!runFinishFunction(kWasiPrintHex).has_value());
BEAST_EXPECT(!runFinish(kWasiGetTimeHex).has_value());
BEAST_EXPECT(!runFinish(kWasiPrintHex).has_value());
}
void
testWasmSectionCorruption()
{
testcase("Wasm Section Corruption tests");
BEAST_EXPECT(!runFinishFunction(kBadMagicNumberHex).has_value());
BEAST_EXPECT(!runFinishFunction(kBadVersionNumberHex).has_value());
BEAST_EXPECT(!runFinishFunction(kLyingHeaderHex).has_value());
BEAST_EXPECT(!runFinishFunction(kNeverEndingNumberHex).has_value());
BEAST_EXPECT(!runFinishFunction(kVectorLieHex).has_value());
BEAST_EXPECT(!runFinishFunction(kSectionOrderingHex).has_value());
BEAST_EXPECT(!runFinishFunction(kGhostPayloadHex).has_value());
BEAST_EXPECT(!runFinishFunction(kJunkAfterSectionHex).has_value());
BEAST_EXPECT(!runFinishFunction(kInvalidSectionIdHex).has_value());
BEAST_EXPECT(!runFinishFunction(kLocalVariableBombHex).has_value());
BEAST_EXPECT(!runFinish(kBadMagicNumberHex).has_value());
BEAST_EXPECT(!runFinish(kBadVersionNumberHex).has_value());
BEAST_EXPECT(!runFinish(kLyingHeaderHex).has_value());
BEAST_EXPECT(!runFinish(kNeverEndingNumberHex).has_value());
BEAST_EXPECT(!runFinish(kVectorLieHex).has_value());
BEAST_EXPECT(!runFinish(kSectionOrderingHex).has_value());
BEAST_EXPECT(!runFinish(kGhostPayloadHex).has_value());
BEAST_EXPECT(!runFinish(kJunkAfterSectionHex).has_value());
BEAST_EXPECT(!runFinish(kInvalidSectionIdHex).has_value());
BEAST_EXPECT(!runFinish(kLocalVariableBombHex).has_value());
}
void

View File

@@ -2,7 +2,108 @@
#include <test/app/wasm_fixtures/fixtures.h>
#include <cstdint>
#include <string>
#include <vector>
namespace wasm_constants {
namespace {
void
appendU32Leb(std::vector<uint8_t>& out, uint32_t value)
{
do
{
auto byte = static_cast<uint8_t>(value & 0x7f);
value >>= 7;
if (value != 0u)
byte |= 0x80;
out.push_back(byte);
} while (value != 0u);
}
void
appendSection(std::vector<uint8_t>& out, uint8_t section, std::vector<uint8_t> const& payload)
{
out.push_back(section);
appendU32Leb(out, payload.size());
out.insert(out.end(), payload.begin(), payload.end());
}
void
appendBytes(std::vector<uint8_t>& out, auto const& bytes)
{
for (auto byte : bytes)
out.push_back(byte);
}
std::vector<uint8_t>
baseModule()
{
std::vector<uint8_t> out;
appendBytes(out, kWasmHeader);
appendBytes(out, kTypeEmptyFunc);
appendBytes(out, kFuncTypE0);
appendBytes(out, kExportFinish);
return out;
}
} // namespace
std::vector<uint8_t>
generateCodeBlob(uint32_t numInstructions)
{
auto out = baseModule();
std::vector<uint8_t> body;
body.push_back(0x00);
body.insert(body.end(), numInstructions, kInstrNop);
body.push_back(kInstrEnd);
std::vector<uint8_t> codePayload;
codePayload.push_back(0x01);
appendU32Leb(codePayload, body.size());
codePayload.insert(codePayload.end(), body.begin(), body.end());
appendSection(out, kSectionCode, codePayload);
return out;
}
std::vector<uint8_t>
generateDataBlob(uint32_t dataSize)
{
std::vector<uint8_t> out;
appendBytes(out, kWasmHeader);
appendBytes(out, kTypeEmptyFunc);
appendBytes(out, kFuncTypE0);
std::vector<uint8_t> memoryPayload;
memoryPayload.push_back(0x01);
memoryPayload.push_back(0x00);
appendU32Leb(memoryPayload, (dataSize + 65'535) / 65'536);
appendSection(out, kSectionMemory, memoryPayload);
appendBytes(out, kExportFinish);
std::vector<uint8_t> codePayload;
codePayload.push_back(0x01);
appendU32Leb(codePayload, sizeof(kEmptyBody));
appendBytes(codePayload, kEmptyBody);
appendSection(out, kSectionCode, codePayload);
std::vector<uint8_t> dataPayload;
dataPayload.push_back(0x01);
dataPayload.push_back(0x00);
appendBytes(dataPayload, kDataOffsetZero);
appendU32Leb(dataPayload, dataSize);
dataPayload.insert(dataPayload.end(), dataSize, kDataFillByte);
appendSection(out, kSectionData, dataPayload);
return out;
}
} // namespace wasm_constants
extern std::string const kFibWasmHex =
"0061736d0100000001090260000060017f017f030302000105030100020638097f004180080b7f004180080b7f0041"
@@ -1392,3 +1493,18 @@ extern std::string const kImpExpHex =
"6e000003656e76166765745f706172656e745f6c65646765725f686173680000030403010201050301000107310406"
"6d656d6f72790200096578705f66756e63310002096578705f66756e633200030c746573745f696d706f7274730004"
"0a2b03040041010b0700200041026c0b1c01027f4120410410001a41202802002100410041201001210120000b";
extern std::string const kUpdateDataWasmHex =
"0061736d01000000010e0360027f7f017f6000006000017f02100103656e76087365745f6461746100000303020102"
"0503010002063f0a7f01419088040b7f004180080b7f004185080b7f004190080b7f00419088040b7f004180080b7f"
"00419088040b7f00418080080b7f0041000b7f0041010b07b1010c066d656d6f72790200115f5f7761736d5f63616c"
"6c5f63746f727300010d657363726f775f66696e69736800020c5f5f64736f5f68616e646c6503010a5f5f64617461"
"5f656e6403020b5f5f737461636b5f6c6f7703030c5f5f737461636b5f6869676803040d5f5f676c6f62616c5f6261"
"736503050b5f5f686561705f6261736503060a5f5f686561705f656e6403070d5f5f6d656d6f72795f626173650308"
"0c5f5f7461626c655f6261736503090a3f0202000b3a01017f230041106b220024002000410c6a4184082d00003a00"
"002000418008280000360208200041086a410410001a200041106a240041807e0b0b0b01004180080b044461746100"
"7f0970726f647563657273010c70726f6365737365642d62790105636c616e675f31392e312e352d776173692d7364"
"6b202868747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a6563742061623462356132"
"6462353832393538616631656533303861373930636664623432626432343732302900490f7461726765745f666561"
"7475726573042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d7479"
"7065732b0a6d756c746976616c7565";

View File

@@ -2,7 +2,80 @@
// TODO: consider moving these to separate files (and figure out the build)
#include <cstdint>
#include <string>
#include <vector>
// WASM binary format constants and helpers for building test modules
namespace wasm_constants {
// Magic + version header
uint8_t const kWasmHeader[] = {
0x00,
0x61,
0x73,
0x6d, // magic: \0asm
0x01,
0x00,
0x00,
0x00 // version: 1
};
// Type section: () -> ()
uint8_t const kTypeEmptyFunc[] = {0x01, 0x04, 0x01, 0x60, 0x00, 0x00};
// Function section: one function using type 0
uint8_t const kFuncTypE0[] = {0x03, 0x02, 0x01, 0x00};
// Export section: export func 0 as "escrow_finish"
uint8_t const kExportFinish[] = {
0x07,
0x11,
0x01,
0x0d,
'e',
's',
'c',
'r',
'o',
'w',
'_',
'f',
'i',
'n',
'i',
's',
'h',
0x00,
0x00};
// Empty function body: 0 locals, end
uint8_t const kEmptyBody[] = {0x00, 0x0b};
// Data segment offset: i32.const 0, end
uint8_t const kDataOffsetZero[] = {0x41, 0x00, 0x0b};
// Section IDs
uint8_t const kSectionMemory = 0x05;
uint8_t const kSectionCode = 0x0a;
uint8_t const kSectionData = 0x0b;
// Instructions
uint8_t const kInstrNop = 0x01;
uint8_t const kInstrEnd = 0x0b;
// Fill byte for data section bloat
uint8_t const kDataFillByte = 0xEE;
// Generator for WASM module with large code section (many NOPs)
std::vector<uint8_t>
generateCodeBlob(uint32_t numInstructions);
// Generator for WASM module with large data section
std::vector<uint8_t>
generateDataBlob(uint32_t dataSize);
} // namespace wasm_constants
extern std::string const kLedgerSqnWasmHex;
extern std::string const kAllHostFunctionsWasmHex;
@@ -81,3 +154,4 @@ extern std::string const kFunctions5kHex;
extern std::string const kOpcReservedHex;
extern std::string const kImpExpHex;
extern std::string const kUpdateDataWasmHex;

View File

@@ -0,0 +1,11 @@
#include <stdint.h>
int32_t set_data(uint8_t const *, int32_t);
int escrow_finish()
{
uint8_t buf[] = "Data";
set_data(buf, sizeof(buf) - 1);
return -256;
}

View File

@@ -2,8 +2,11 @@
#include <test/jtx/Account.h>
#include <test/jtx/Env.h>
#include <test/jtx/JTx.h>
#include <test/jtx/TestHelpers.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Rate.h>
@@ -11,7 +14,10 @@
#include <xrpl/protocol/STAmount.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
/**
* Escrow operations.
@@ -86,4 +92,73 @@ auto const kCondition = JTxFieldWrapper<BlobField>(sfCondition);
auto const kFulfillment = JTxFieldWrapper<BlobField>(sfFulfillment);
struct Bytecode
{
private:
std::string value_;
public:
explicit Bytecode(std::string func) : value_(std::move(func))
{
}
explicit Bytecode(Slice const& func) : value_(strHex(func))
{
}
template <size_t N>
explicit Bytecode(std::array<std::uint8_t, N> const& f) : Bytecode(makeSlice(f))
{
}
void
operator()(Env&, JTx& jt) const
{
jt.jv[sfBytecode.jsonName] = value_;
}
};
struct Data
{
private:
std::string value_;
public:
explicit Data(std::string func) : value_(std::move(func))
{
}
explicit Data(Slice const& func) : value_(strHex(func))
{
}
template <size_t N>
explicit Data(std::array<std::uint8_t, N> const& f) : Data(makeSlice(f))
{
}
void
operator()(Env&, JTx& jt) const
{
jt.jv[sfData.jsonName] = value_;
}
};
struct Gas
{
private:
std::uint32_t value_;
public:
explicit Gas(std::uint32_t const& value) : value_(value)
{
}
void
operator()(Env&, JTx& jt) const
{
jt.jv[sfGas.jsonName] = value_;
}
};
} // namespace xrpl::test::jtx::escrow

View File

@@ -21,12 +21,14 @@ setupConfigForUnitTests(Config& cfg)
using namespace jtx;
// Default fees to old values, so tests don't have to worry about changes in
// Config.h
// NOTE: For new `fees` fields, you need to wait for the first flag ledger
// to close for the values to be activated.
cfg.fees.referenceFee = UNIT_TEST_REFERENCE_FEE;
cfg.fees.accountReserve = XRP(200).value().xrp().drops();
cfg.fees.ownerReserve = XRP(50).value().xrp().drops();
cfg.fees.gasLimit = 1'000'000;
cfg.fees.bytecodeSizeLimit = 1'000'000;
cfg.fees.gasPrice = 1'000;
cfg.fees.bytecodeSizeLimit = 100'000;
cfg.fees.gasPrice = 1'000'000; // 1 drop = 1,000,000 micro-drops
// The Beta API (currently v2) is always available to tests
cfg.betaRpcApi = true;

View File

@@ -8,6 +8,7 @@
#include <xrpl/core/NetworkIDService.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/PendingSaves.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/server/LoadFeeTrack.h>
#include <boost/asio/io_context.hpp>
@@ -71,11 +72,22 @@ private:
*/
class TestServiceRegistry : public ServiceRegistry
{
static Fees
defaultFees()
{
Fees fees{XRPAmount{10}, XRPAmount{10 * kDropsPerXrp}, XRPAmount{2 * kDropsPerXrp}};
fees.gasLimit = 1'000'000;
fees.bytecodeSizeLimit = 100'000;
fees.gasPrice = 1'000'000;
return fees;
}
TestLogs logs_{beast::Severity::Warning};
boost::asio::io_context ioContext_;
TestFamily family_{logs_.journal("TestFamily")};
LoadFeeTrack feeTrack_{logs_.journal("LoadFeeTrack")};
TestNetworkIDService networkIDService_;
Fees fees_{defaultFees()};
HashRouter hashRouter_{HashRouter::Setup{}, stopwatch()};
NodeCache tempNodeCache_{
"TempNodeCache",
@@ -374,6 +386,12 @@ public:
throw std::logic_error("TestServiceRegistry::getWalletDB() not implemented");
}
Fees
getFees() const override
{
return fees_;
}
// Temporary: Get the underlying Application
Application&
getApp() override

View File

@@ -27,6 +27,8 @@ TEST(EscrowTests, BuilderSettersRoundTrip)
auto const conditionValue = canonical_VL();
auto const cancelAfterValue = canonical_UINT32();
auto const finishAfterValue = canonical_UINT32();
auto const bytecodeValue = canonical_VL();
auto const dataValue = canonical_VL();
auto const sourceTagValue = canonical_UINT32();
auto const destinationTagValue = canonical_UINT32();
auto const ownerNodeValue = canonical_UINT64();
@@ -49,6 +51,8 @@ TEST(EscrowTests, BuilderSettersRoundTrip)
builder.setCondition(conditionValue);
builder.setCancelAfter(cancelAfterValue);
builder.setFinishAfter(finishAfterValue);
builder.setBytecode(bytecodeValue);
builder.setData(dataValue);
builder.setSourceTag(sourceTagValue);
builder.setDestinationTag(destinationTagValue);
builder.setDestinationNode(destinationNodeValue);
@@ -132,6 +136,22 @@ TEST(EscrowTests, BuilderSettersRoundTrip)
EXPECT_TRUE(entry.hasFinishAfter());
}
{
auto const& expected = bytecodeValue;
auto const actualOpt = entry.getBytecode();
ASSERT_TRUE(actualOpt.has_value());
expectEqualField(expected, *actualOpt, "sfBytecode");
EXPECT_TRUE(entry.hasBytecode());
}
{
auto const& expected = dataValue;
auto const actualOpt = entry.getData();
ASSERT_TRUE(actualOpt.has_value());
expectEqualField(expected, *actualOpt, "sfData");
EXPECT_TRUE(entry.hasData());
}
{
auto const& expected = sourceTagValue;
auto const actualOpt = entry.getSourceTag();
@@ -192,6 +212,8 @@ TEST(EscrowTests, BuilderFromSleRoundTrip)
auto const conditionValue = canonical_VL();
auto const cancelAfterValue = canonical_UINT32();
auto const finishAfterValue = canonical_UINT32();
auto const bytecodeValue = canonical_VL();
auto const dataValue = canonical_VL();
auto const sourceTagValue = canonical_UINT32();
auto const destinationTagValue = canonical_UINT32();
auto const ownerNodeValue = canonical_UINT64();
@@ -210,6 +232,8 @@ TEST(EscrowTests, BuilderFromSleRoundTrip)
sle->at(sfCondition) = conditionValue;
sle->at(sfCancelAfter) = cancelAfterValue;
sle->at(sfFinishAfter) = finishAfterValue;
sle->at(sfBytecode) = bytecodeValue;
sle->at(sfData) = dataValue;
sle->at(sfSourceTag) = sourceTagValue;
sle->at(sfDestinationTag) = destinationTagValue;
sle->at(sfOwnerNode) = ownerNodeValue;
@@ -340,6 +364,32 @@ TEST(EscrowTests, BuilderFromSleRoundTrip)
expectEqualField(expected, *fromBuilderOpt, "sfFinishAfter");
}
{
auto const& expected = bytecodeValue;
auto const fromSleOpt = entryFromSle.getBytecode();
auto const fromBuilderOpt = entryFromBuilder.getBytecode();
ASSERT_TRUE(fromSleOpt.has_value());
ASSERT_TRUE(fromBuilderOpt.has_value());
expectEqualField(expected, *fromSleOpt, "sfBytecode");
expectEqualField(expected, *fromBuilderOpt, "sfBytecode");
}
{
auto const& expected = dataValue;
auto const fromSleOpt = entryFromSle.getData();
auto const fromBuilderOpt = entryFromBuilder.getData();
ASSERT_TRUE(fromSleOpt.has_value());
ASSERT_TRUE(fromBuilderOpt.has_value());
expectEqualField(expected, *fromSleOpt, "sfData");
expectEqualField(expected, *fromBuilderOpt, "sfData");
}
{
auto const& expected = sourceTagValue;
@@ -477,6 +527,10 @@ TEST(EscrowTests, OptionalFieldsReturnNullopt)
EXPECT_FALSE(entry.getCancelAfter().has_value());
EXPECT_FALSE(entry.hasFinishAfter());
EXPECT_FALSE(entry.getFinishAfter().has_value());
EXPECT_FALSE(entry.hasBytecode());
EXPECT_FALSE(entry.getBytecode().has_value());
EXPECT_FALSE(entry.hasData());
EXPECT_FALSE(entry.getData().has_value());
EXPECT_FALSE(entry.hasSourceTag());
EXPECT_FALSE(entry.getSourceTag().has_value());
EXPECT_FALSE(entry.hasDestinationTag());

View File

@@ -30,11 +30,13 @@ TEST(TransactionsEscrowCreateTests, BuilderSettersRoundTrip)
// Transaction-specific field values
auto const destinationValue = canonical_ACCOUNT();
auto const destinationTagValue = canonical_UINT32();
auto const amountValue = canonical_AMOUNT();
auto const conditionValue = canonical_VL();
auto const cancelAfterValue = canonical_UINT32();
auto const finishAfterValue = canonical_UINT32();
auto const destinationTagValue = canonical_UINT32();
auto const bytecodeValue = canonical_VL();
auto const dataValue = canonical_VL();
EscrowCreateBuilder builder{
accountValue,
@@ -45,10 +47,12 @@ TEST(TransactionsEscrowCreateTests, BuilderSettersRoundTrip)
};
// Set optional fields
builder.setDestinationTag(destinationTagValue);
builder.setCondition(conditionValue);
builder.setCancelAfter(cancelAfterValue);
builder.setFinishAfter(finishAfterValue);
builder.setDestinationTag(destinationTagValue);
builder.setBytecode(bytecodeValue);
builder.setData(dataValue);
auto tx = builder.build(publicKey, secretKey);
@@ -78,6 +82,14 @@ TEST(TransactionsEscrowCreateTests, BuilderSettersRoundTrip)
}
// Verify optional fields
{
auto const& expected = destinationTagValue;
auto const actualOpt = tx.getDestinationTag();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present";
expectEqualField(expected, *actualOpt, "sfDestinationTag");
EXPECT_TRUE(tx.hasDestinationTag());
}
{
auto const& expected = conditionValue;
auto const actualOpt = tx.getCondition();
@@ -103,11 +115,19 @@ TEST(TransactionsEscrowCreateTests, BuilderSettersRoundTrip)
}
{
auto const& expected = destinationTagValue;
auto const actualOpt = tx.getDestinationTag();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present";
expectEqualField(expected, *actualOpt, "sfDestinationTag");
EXPECT_TRUE(tx.hasDestinationTag());
auto const& expected = bytecodeValue;
auto const actualOpt = tx.getBytecode();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBytecode should be present";
expectEqualField(expected, *actualOpt, "sfBytecode");
EXPECT_TRUE(tx.hasBytecode());
}
{
auto const& expected = dataValue;
auto const actualOpt = tx.getData();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present";
expectEqualField(expected, *actualOpt, "sfData");
EXPECT_TRUE(tx.hasData());
}
}
@@ -127,11 +147,13 @@ TEST(TransactionsEscrowCreateTests, BuilderFromStTxRoundTrip)
// Transaction-specific field values
auto const destinationValue = canonical_ACCOUNT();
auto const destinationTagValue = canonical_UINT32();
auto const amountValue = canonical_AMOUNT();
auto const conditionValue = canonical_VL();
auto const cancelAfterValue = canonical_UINT32();
auto const finishAfterValue = canonical_UINT32();
auto const destinationTagValue = canonical_UINT32();
auto const bytecodeValue = canonical_VL();
auto const dataValue = canonical_VL();
// Build an initial transaction
EscrowCreateBuilder initialBuilder{
@@ -142,10 +164,12 @@ TEST(TransactionsEscrowCreateTests, BuilderFromStTxRoundTrip)
feeValue
};
initialBuilder.setDestinationTag(destinationTagValue);
initialBuilder.setCondition(conditionValue);
initialBuilder.setCancelAfter(cancelAfterValue);
initialBuilder.setFinishAfter(finishAfterValue);
initialBuilder.setDestinationTag(destinationTagValue);
initialBuilder.setBytecode(bytecodeValue);
initialBuilder.setData(dataValue);
auto initialTx = initialBuilder.build(publicKey, secretKey);
@@ -176,6 +200,13 @@ TEST(TransactionsEscrowCreateTests, BuilderFromStTxRoundTrip)
}
// Verify optional fields
{
auto const& expected = destinationTagValue;
auto const actualOpt = rebuiltTx.getDestinationTag();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present";
expectEqualField(expected, *actualOpt, "sfDestinationTag");
}
{
auto const& expected = conditionValue;
auto const actualOpt = rebuiltTx.getCondition();
@@ -198,10 +229,17 @@ TEST(TransactionsEscrowCreateTests, BuilderFromStTxRoundTrip)
}
{
auto const& expected = destinationTagValue;
auto const actualOpt = rebuiltTx.getDestinationTag();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfDestinationTag should be present";
expectEqualField(expected, *actualOpt, "sfDestinationTag");
auto const& expected = bytecodeValue;
auto const actualOpt = rebuiltTx.getBytecode();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBytecode should be present";
expectEqualField(expected, *actualOpt, "sfBytecode");
}
{
auto const& expected = dataValue;
auto const actualOpt = rebuiltTx.getData();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present";
expectEqualField(expected, *actualOpt, "sfData");
}
}
@@ -263,14 +301,18 @@ TEST(TransactionsEscrowCreateTests, OptionalFieldsReturnNullopt)
auto tx = builder.build(publicKey, secretKey);
// Verify optional fields are not present
EXPECT_FALSE(tx.hasDestinationTag());
EXPECT_FALSE(tx.getDestinationTag().has_value());
EXPECT_FALSE(tx.hasCondition());
EXPECT_FALSE(tx.getCondition().has_value());
EXPECT_FALSE(tx.hasCancelAfter());
EXPECT_FALSE(tx.getCancelAfter().has_value());
EXPECT_FALSE(tx.hasFinishAfter());
EXPECT_FALSE(tx.getFinishAfter().has_value());
EXPECT_FALSE(tx.hasDestinationTag());
EXPECT_FALSE(tx.getDestinationTag().has_value());
EXPECT_FALSE(tx.hasBytecode());
EXPECT_FALSE(tx.getBytecode().has_value());
EXPECT_FALSE(tx.hasData());
EXPECT_FALSE(tx.getData().has_value());
}
}

View File

@@ -34,6 +34,7 @@ TEST(TransactionsEscrowFinishTests, BuilderSettersRoundTrip)
auto const fulfillmentValue = canonical_VL();
auto const conditionValue = canonical_VL();
auto const credentialIDsValue = canonical_VECTOR256();
auto const gasValue = canonical_UINT32();
EscrowFinishBuilder builder{
accountValue,
@@ -47,6 +48,7 @@ TEST(TransactionsEscrowFinishTests, BuilderSettersRoundTrip)
builder.setFulfillment(fulfillmentValue);
builder.setCondition(conditionValue);
builder.setCredentialIDs(credentialIDsValue);
builder.setGas(gasValue);
auto tx = builder.build(publicKey, secretKey);
@@ -100,6 +102,14 @@ TEST(TransactionsEscrowFinishTests, BuilderSettersRoundTrip)
EXPECT_TRUE(tx.hasCredentialIDs());
}
{
auto const& expected = gasValue;
auto const actualOpt = tx.getGas();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfGas should be present";
expectEqualField(expected, *actualOpt, "sfGas");
EXPECT_TRUE(tx.hasGas());
}
}
// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper,
@@ -121,6 +131,7 @@ TEST(TransactionsEscrowFinishTests, BuilderFromStTxRoundTrip)
auto const fulfillmentValue = canonical_VL();
auto const conditionValue = canonical_VL();
auto const credentialIDsValue = canonical_VECTOR256();
auto const gasValue = canonical_UINT32();
// Build an initial transaction
EscrowFinishBuilder initialBuilder{
@@ -134,6 +145,7 @@ TEST(TransactionsEscrowFinishTests, BuilderFromStTxRoundTrip)
initialBuilder.setFulfillment(fulfillmentValue);
initialBuilder.setCondition(conditionValue);
initialBuilder.setCredentialIDs(credentialIDsValue);
initialBuilder.setGas(gasValue);
auto initialTx = initialBuilder.build(publicKey, secretKey);
@@ -185,6 +197,13 @@ TEST(TransactionsEscrowFinishTests, BuilderFromStTxRoundTrip)
expectEqualField(expected, *actualOpt, "sfCredentialIDs");
}
{
auto const& expected = gasValue;
auto const actualOpt = rebuiltTx.getGas();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfGas should be present";
expectEqualField(expected, *actualOpt, "sfGas");
}
}
// 3) Verify wrapper throws when constructed from wrong transaction type.
@@ -250,6 +269,8 @@ TEST(TransactionsEscrowFinishTests, OptionalFieldsReturnNullopt)
EXPECT_FALSE(tx.getCondition().has_value());
EXPECT_FALSE(tx.hasCredentialIDs());
EXPECT_FALSE(tx.getCredentialIDs().has_value());
EXPECT_FALSE(tx.hasGas());
EXPECT_FALSE(tx.getGas().has_value());
}
}

View File

@@ -826,6 +826,24 @@ public:
return *walletDB_;
}
Fees
getFees() const override
{
XRPL_ASSERT(config_, "xrpl::ApplicationImp::getFees : non-null config");
auto const& f1(config_->fees);
Fees f2;
f2.base = f1.referenceFee;
f2.reserve = f1.accountReserve;
f2.increment = f1.ownerReserve;
f2.gasLimit = f1.gasLimit;
f2.bytecodeSizeLimit = f1.bytecodeSizeLimit;
f2.gasPrice = f1.gasPrice;
return f2;
}
bool
serverOkay(std::string& reason) override;